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 * boxfunc2.c 00018 * 00019 * Boxa/Box transform (shift, scale) and orthogonal rotation 00020 * BOXA *boxaTransform() 00021 * BOX *boxTransform() 00022 * BOXA *boxaTransformOrdered() 00023 * BOX *boxTransformOrdered() 00024 * BOXA *boxaRotateOrth() 00025 * BOX *boxRotateOrth() 00026 * 00027 * Boxa sort 00028 * BOXA *boxaSort() 00029 * BOXA *boxaBinSort() 00030 * BOXA *boxaSortByIndex() 00031 * BOXAA *boxaSort2d() 00032 * BOXAA *boxaSort2dByIndex() 00033 * 00034 * Boxa statistics 00035 * BOX *boxaGetRankSize() 00036 * BOX *boxaGetMedian() 00037 * 00038 * Other boxaa functions 00039 * l_int32 boxaaGetExtent() 00040 * BOXA *boxaaFlattenToBoxa() 00041 * l_int32 boxaaAlignBox() 00042 */ 00043 00044 #include <stdio.h> 00045 #include <stdlib.h> 00046 #include <math.h> 00047 #include "allheaders.h" 00048 00049 /* For more than this number of c.c. in a binarized image of 00050 * semi-perimeter (w + h) about 5000 or less, the O(n) binsort 00051 * is faster than the O(nlogn) shellsort. */ 00052 static const l_int32 MIN_COMPS_FOR_BIN_SORT = 500; 00053 00054 00055 /*---------------------------------------------------------------------* 00056 * Boxa/Box transform (shift, scale) and orthogonal rotation * 00057 *---------------------------------------------------------------------*/ 00058 /*! 00059 * boxaTransform() 00060 * 00061 * Input: boxa 00062 * shiftx, shifty 00063 * scalex, scaley 00064 * Return: boxad, or null on error 00065 * 00066 * Notes: 00067 * (1) This is a very simple function that first shifts, then scales. 00068 */ 00069 BOXA * 00070 boxaTransform(BOXA *boxas, 00071 l_int32 shiftx, 00072 l_int32 shifty, 00073 l_float32 scalex, 00074 l_float32 scaley) 00075 { 00076 l_int32 i, n; 00077 BOX *boxs, *boxd; 00078 BOXA *boxad; 00079 00080 PROCNAME("boxaTransform"); 00081 00082 if (!boxas) 00083 return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); 00084 n = boxaGetCount(boxas); 00085 if ((boxad = boxaCreate(n)) == NULL) 00086 return (BOXA *)ERROR_PTR("boxad not made", procName, NULL); 00087 for (i = 0; i < n; i++) { 00088 if ((boxs = boxaGetBox(boxas, i, L_CLONE)) == NULL) 00089 return (BOXA *)ERROR_PTR("boxs not found", procName, NULL); 00090 boxd = boxTransform(boxs, shiftx, shifty, scalex, scaley); 00091 boxDestroy(&boxs); 00092 boxaAddBox(boxad, boxd, L_INSERT); 00093 } 00094 00095 return boxad; 00096 } 00097 00098 00099 /*! 00100 * boxTransform() 00101 * 00102 * Input: box 00103 * shiftx, shifty 00104 * scalex, scaley 00105 * Return: boxd, or null on error 00106 * 00107 * Notes: 00108 * (1) This is a very simple function that first shifts, then scales. 00109 */ 00110 BOX * 00111 boxTransform(BOX *box, 00112 l_int32 shiftx, 00113 l_int32 shifty, 00114 l_float32 scalex, 00115 l_float32 scaley) 00116 { 00117 PROCNAME("boxTransform"); 00118 00119 if (!box) 00120 return (BOX *)ERROR_PTR("box not defined", procName, NULL); 00121 return boxCreate((l_int32)(scalex * (box->x + shiftx) + 0.5), 00122 (l_int32)(scaley * (box->y + shifty) + 0.5), 00123 (l_int32)(L_MAX(1.0, scalex * box->w + 0.5)), 00124 (l_int32)(L_MAX(1.0, scaley * box->h + 0.5))); 00125 } 00126 00127 00128 /*! 00129 * boxaTransformOrdered() 00130 * 00131 * Input: boxa 00132 * shiftx, shifty 00133 * scalex, scaley 00134 * xcen, ycen (center of rotation) 00135 * angle (in radians; clockwise is positive) 00136 * order (one of 6 combinations: L_TR_SC_RO, ...) 00137 * Return: boxd, or null on error 00138 * 00139 * Notes: 00140 * (1) This allows a sequence of linear transforms on each box. 00141 * the transforms are from the affine set, composed of 00142 * shift, scaling and rotation, and the order of the 00143 * transforms is specified. 00144 * (2) Although these operations appear to be on an infinite 00145 * 2D plane, in practice the region of interest is clipped 00146 * to a finite image. The center of rotation is usually taken 00147 * with respect to the image (either the UL corner or the 00148 * center). A translation can have two very different effects: 00149 * (a) Moves the boxes across the fixed image region. 00150 * (b) Moves the image origin, causing a change in the image 00151 * region and an opposite effective translation of the boxes. 00152 * This function should only be used for (a), where the image 00153 * region is fixed on translation. If the image region is 00154 * changed by the translation, use instead the functions 00155 * in affinecompose.c, where the image region and rotation 00156 * center can be computed from the actual clipping due to 00157 * translation of the image origin. 00158 * (3) See boxTransformOrdered() for usage and implementation details. 00159 */ 00160 BOXA * 00161 boxaTransformOrdered(BOXA *boxas, 00162 l_int32 shiftx, 00163 l_int32 shifty, 00164 l_float32 scalex, 00165 l_float32 scaley, 00166 l_int32 xcen, 00167 l_int32 ycen, 00168 l_float32 angle, 00169 l_int32 order) 00170 { 00171 l_int32 i, n; 00172 BOX *boxs, *boxd; 00173 BOXA *boxad; 00174 00175 PROCNAME("boxaTransformOrdered"); 00176 00177 if (!boxas) 00178 return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); 00179 n = boxaGetCount(boxas); 00180 if ((boxad = boxaCreate(n)) == NULL) 00181 return (BOXA *)ERROR_PTR("boxad not made", procName, NULL); 00182 for (i = 0; i < n; i++) { 00183 if ((boxs = boxaGetBox(boxas, i, L_CLONE)) == NULL) 00184 return (BOXA *)ERROR_PTR("boxs not found", procName, NULL); 00185 boxd = boxTransformOrdered(boxs, shiftx, shifty, scalex, scaley, 00186 xcen, ycen, angle, order); 00187 boxDestroy(&boxs); 00188 boxaAddBox(boxad, boxd, L_INSERT); 00189 } 00190 00191 return boxad; 00192 } 00193 00194 00195 /*! 00196 * boxTransformOrdered() 00197 * 00198 * Input: boxs 00199 * shiftx, shifty 00200 * scalex, scaley 00201 * xcen, ycen (center of rotation) 00202 * angle (in radians; clockwise is positive) 00203 * order (one of 6 combinations: L_TR_SC_RO, ...) 00204 * Return: boxd, or null on error 00205 * 00206 * Notes: 00207 * (1) This allows a sequence of linear transforms, composed of 00208 * shift, scaling and rotation, where the order of the 00209 * transforms is specified. 00210 * (2) The rotation is taken about a point specified by (xcen, ycen). 00211 * Let the components of the vector from the center of rotation 00212 * to the box center be (xdif, ydif): 00213 * xdif = (bx + 0.5 * bw) - xcen 00214 * ydif = (by + 0.5 * bh) - ycen 00215 * Then the box center after rotation has new components: 00216 * bxcen = xcen + xdif * cosa + ydif * sina 00217 * bycen = ycen + ydif * cosa - xdif * sina 00218 * where cosa and sina are the cos and sin of the angle, 00219 * and the enclosing box for the rotated box has size: 00220 * rw = |bw * cosa| + |bh * sina| 00221 * rh = |bh * cosa| + |bw * sina| 00222 * where bw and bh are the unrotated width and height. 00223 * Then the box UL corner (rx, ry) is 00224 * rx = bxcen - 0.5 * rw 00225 * ry = bycen - 0.5 * rh 00226 * (3) The center of rotation specified by args @xcen and @ycen 00227 * is the point BEFORE any translation or scaling. If the 00228 * rotation is not the first operation, this function finds 00229 * the actual center at the time of rotation. It does this 00230 * by making the following assumptions: 00231 * (1) Any scaling is with respect to the UL corner, so 00232 * that the center location scales accordingly. 00233 * (2) A translation does not affect the center of 00234 * the image; it just moves the boxes. 00235 * We always use assumption (1). However, assumption (2) 00236 * will be incorrect if the apparent translation is due 00237 * to a clipping operation that, in effect, moves the 00238 * origin of the image. In that case, you should NOT use 00239 * these simple functions. Instead, use the functions 00240 * in affinecompose.c, where the rotation center can be 00241 * computed from the actual clipping due to translation 00242 * of the image origin. 00243 */ 00244 BOX * 00245 boxTransformOrdered(BOX *boxs, 00246 l_int32 shiftx, 00247 l_int32 shifty, 00248 l_float32 scalex, 00249 l_float32 scaley, 00250 l_int32 xcen, 00251 l_int32 ycen, 00252 l_float32 angle, 00253 l_int32 order) 00254 { 00255 l_int32 bx, by, bw, bh, tx, ty, tw, th; 00256 l_int32 xcent, ycent; /* transformed center of rotation due to scaling */ 00257 l_float32 sina, cosa, xdif, ydif, rx, ry, rw, rh; 00258 BOX *boxd; 00259 00260 PROCNAME("boxTransformOrdered"); 00261 00262 if (!boxs) 00263 return (BOX *)ERROR_PTR("boxs not defined", procName, NULL); 00264 if (order != L_TR_SC_RO && order != L_SC_RO_TR && order != L_RO_TR_SC && 00265 order != L_TR_RO_SC && order != L_RO_SC_TR && order != L_SC_TR_RO) 00266 return (BOX *)ERROR_PTR("order invalid", procName, NULL); 00267 00268 boxGetGeometry(boxs, &bx, &by, &bw, &bh); 00269 if (angle != 0.0) { 00270 sina = sin(angle); 00271 cosa = cos(angle); 00272 } 00273 00274 if (order == L_TR_SC_RO) { 00275 tx = (l_int32)(scalex * (bx + shiftx) + 0.5); 00276 ty = (l_int32)(scaley * (by + shifty) + 0.5); 00277 tw = (l_int32)(L_MAX(1.0, scalex * bw + 0.5)); 00278 th = (l_int32)(L_MAX(1.0, scaley * bh + 0.5)); 00279 xcent = (l_int32)(scalex * xcen + 0.5); 00280 ycent = (l_int32)(scaley * ycen + 0.5); 00281 if (angle == 0.0) 00282 boxd = boxCreate(tx, ty, tw, th); 00283 else { 00284 xdif = tx + 0.5 * tw - xcent; 00285 ydif = ty + 0.5 * th - ycent; 00286 rw = L_ABS(tw * cosa) + L_ABS(th * sina); 00287 rh = L_ABS(th * cosa) + L_ABS(tw * sina); 00288 rx = xcent + xdif * cosa - ydif * sina - 0.5 * rw; 00289 ry = ycent + ydif * cosa + xdif * sina - 0.5 * rh; 00290 boxd = boxCreate((l_int32)rx, (l_int32)ry, (l_int32)rw, 00291 (l_int32)rh); 00292 } 00293 } 00294 else if (order == L_SC_TR_RO) { 00295 tx = (l_int32)(scalex * bx + shiftx + 0.5); 00296 ty = (l_int32)(scaley * by + shifty + 0.5); 00297 tw = (l_int32)(L_MAX(1.0, scalex * bw + 0.5)); 00298 th = (l_int32)(L_MAX(1.0, scaley * bh + 0.5)); 00299 xcent = (l_int32)(scalex * xcen + 0.5); 00300 ycent = (l_int32)(scaley * ycen + 0.5); 00301 if (angle == 0.0) 00302 boxd = boxCreate(tx, ty, tw, th); 00303 else { 00304 xdif = tx + 0.5 * tw - xcent; 00305 ydif = ty + 0.5 * th - ycent; 00306 rw = L_ABS(tw * cosa) + L_ABS(th * sina); 00307 rh = L_ABS(th * cosa) + L_ABS(tw * sina); 00308 rx = xcent + xdif * cosa - ydif * sina - 0.5 * rw; 00309 ry = ycent + ydif * cosa + xdif * sina - 0.5 * rh; 00310 boxd = boxCreate((l_int32)rx, (l_int32)ry, (l_int32)rw, 00311 (l_int32)rh); 00312 } 00313 } 00314 else if (order == L_RO_TR_SC) { 00315 if (angle == 0.0) { 00316 rx = bx; 00317 ry = by; 00318 rw = bw; 00319 rh = bh; 00320 } 00321 else { 00322 xdif = bx + 0.5 * bw - xcen; 00323 ydif = by + 0.5 * bh - ycen; 00324 rw = L_ABS(bw * cosa) + L_ABS(bh * sina); 00325 rh = L_ABS(bh * cosa) + L_ABS(bw * sina); 00326 rx = xcen + xdif * cosa - ydif * sina - 0.5 * rw; 00327 ry = ycen + ydif * cosa + xdif * sina - 0.5 * rh; 00328 } 00329 tx = (l_int32)(scalex * (rx + shiftx) + 0.5); 00330 ty = (l_int32)(scaley * (ry + shifty) + 0.5); 00331 tw = (l_int32)(L_MAX(1.0, scalex * rw + 0.5)); 00332 th = (l_int32)(L_MAX(1.0, scaley * rh + 0.5)); 00333 boxd = boxCreate(tx, ty, tw, th); 00334 } 00335 else if (order == L_RO_SC_TR) { 00336 if (angle == 0.0) { 00337 rx = bx; 00338 ry = by; 00339 rw = bw; 00340 rh = bh; 00341 } 00342 else { 00343 xdif = bx + 0.5 * bw - xcen; 00344 ydif = by + 0.5 * bh - ycen; 00345 rw = L_ABS(bw * cosa) + L_ABS(bh * sina); 00346 rh = L_ABS(bh * cosa) + L_ABS(bw * sina); 00347 rx = xcen + xdif * cosa - ydif * sina - 0.5 * rw; 00348 ry = ycen + ydif * cosa + xdif * sina - 0.5 * rh; 00349 } 00350 tx = (l_int32)(scalex * rx + shiftx + 0.5); 00351 ty = (l_int32)(scaley * ry + shifty + 0.5); 00352 tw = (l_int32)(L_MAX(1.0, scalex * rw + 0.5)); 00353 th = (l_int32)(L_MAX(1.0, scaley * rh + 0.5)); 00354 boxd = boxCreate(tx, ty, tw, th); 00355 } 00356 else if (order == L_TR_RO_SC) { 00357 tx = bx + shiftx; 00358 ty = by + shifty; 00359 if (angle == 0.0) { 00360 rx = tx; 00361 ry = ty; 00362 rw = bw; 00363 rh = bh; 00364 } 00365 else { 00366 xdif = tx + 0.5 * bw - xcen; 00367 ydif = ty + 0.5 * bh - ycen; 00368 rw = L_ABS(bw * cosa) + L_ABS(bh * sina); 00369 rh = L_ABS(bh * cosa) + L_ABS(bw * sina); 00370 rx = xcen + xdif * cosa - ydif * sina - 0.5 * rw; 00371 ry = ycen + ydif * cosa + xdif * sina - 0.5 * rh; 00372 } 00373 tx = (l_int32)(scalex * rx + 0.5); 00374 ty = (l_int32)(scaley * ry + 0.5); 00375 tw = (l_int32)(L_MAX(1.0, scalex * rw + 0.5)); 00376 th = (l_int32)(L_MAX(1.0, scaley * rh + 0.5)); 00377 boxd = boxCreate(tx, ty, tw, th); 00378 } 00379 else { /* order == L_SC_RO_TR) */ 00380 tx = (l_int32)(scalex * bx + 0.5); 00381 ty = (l_int32)(scaley * by + 0.5); 00382 tw = (l_int32)(L_MAX(1.0, scalex * bw + 0.5)); 00383 th = (l_int32)(L_MAX(1.0, scaley * bh + 0.5)); 00384 xcent = (l_int32)(scalex * xcen + 0.5); 00385 ycent = (l_int32)(scaley * ycen + 0.5); 00386 if (angle == 0.0) { 00387 rx = tx; 00388 ry = ty; 00389 rw = tw; 00390 rh = th; 00391 } 00392 else { 00393 xdif = tx + 0.5 * tw - xcent; 00394 ydif = ty + 0.5 * th - ycent; 00395 rw = L_ABS(tw * cosa) + L_ABS(th * sina); 00396 rh = L_ABS(th * cosa) + L_ABS(tw * sina); 00397 rx = xcent + xdif * cosa - ydif * sina - 0.5 * rw; 00398 ry = ycent + ydif * cosa + xdif * sina - 0.5 * rh; 00399 } 00400 tx = (l_int32)(rx + shiftx + 0.5); 00401 ty = (l_int32)(ry + shifty + 0.5); 00402 tw = (l_int32)(rw + 0.5); 00403 th = (l_int32)(rh + 0.5); 00404 boxd = boxCreate(tx, ty, tw, th); 00405 } 00406 00407 return boxd; 00408 } 00409 00410 00411 /*! 00412 * boxaRotateOrth() 00413 * 00414 * Input: boxa 00415 * w, h (of image in which the boxa is embedded) 00416 * rotation (0 = noop, 1 = 90 deg, 2 = 180 deg, 3 = 270 deg; 00417 * all rotations are clockwise) 00418 * Return: boxad, or null on error 00419 * 00420 * Notes: 00421 * (1) See boxRotateOrth() for details. 00422 */ 00423 BOXA * 00424 boxaRotateOrth(BOXA *boxas, 00425 l_int32 w, 00426 l_int32 h, 00427 l_int32 rotation) 00428 { 00429 l_int32 i, n; 00430 BOX *boxs, *boxd; 00431 BOXA *boxad; 00432 00433 PROCNAME("boxaRotateOrth"); 00434 00435 if (!boxas) 00436 return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); 00437 if (rotation == 0) 00438 return boxaCopy(boxas, L_COPY); 00439 if (rotation < 1 || rotation > 3) 00440 return (BOXA *)ERROR_PTR("rotation not in {0,1,2,3}", procName, NULL); 00441 00442 n = boxaGetCount(boxas); 00443 if ((boxad = boxaCreate(n)) == NULL) 00444 return (BOXA *)ERROR_PTR("boxad not made", procName, NULL); 00445 for (i = 0; i < n; i++) { 00446 if ((boxs = boxaGetBox(boxas, i, L_CLONE)) == NULL) 00447 return (BOXA *)ERROR_PTR("boxs not found", procName, NULL); 00448 boxd = boxRotateOrth(boxs, w, h, rotation); 00449 boxDestroy(&boxs); 00450 boxaAddBox(boxad, boxd, L_INSERT); 00451 } 00452 00453 return boxad; 00454 } 00455 00456 00457 /*! 00458 * boxRotateOrth() 00459 * 00460 * Input: box 00461 * w, h (of image in which the box is embedded) 00462 * rotation (0 = noop, 1 = 90 deg, 2 = 180 deg, 3 = 270 deg; 00463 * all rotations are clockwise) 00464 * Return: boxd, or null on error 00465 * 00466 * Notes: 00467 * (1) Rotate the image with the embedded box by the specified amount. 00468 * (2) After rotation, the rotated box is always measured with 00469 * respect to the UL corner of the image. 00470 */ 00471 BOX * 00472 boxRotateOrth(BOX *box, 00473 l_int32 w, 00474 l_int32 h, 00475 l_int32 rotation) 00476 { 00477 l_int32 bx, by, bw, bh, xdist, ydist; 00478 00479 PROCNAME("boxRotateOrth"); 00480 00481 if (!box) 00482 return (BOX *)ERROR_PTR("box not defined", procName, NULL); 00483 if (rotation == 0) 00484 return boxCopy(box); 00485 if (rotation < 1 || rotation > 3) 00486 return (BOX *)ERROR_PTR("rotation not in {0,1,2,3}", procName, NULL); 00487 boxGetGeometry(box, &bx, &by, &bw, &bh); 00488 ydist = h - by - bh; /* below box */ 00489 xdist = w - bx - bw; /* to right of box */ 00490 if (rotation == 1) /* 90 deg cw */ 00491 return boxCreate(ydist, bx, bh, bw); 00492 else if (rotation == 2) /* 180 deg cw */ 00493 return boxCreate(xdist, ydist, bw, bh); 00494 else /* rotation == 3, 270 deg cw */ 00495 return boxCreate(by, xdist, bh, bw); 00496 } 00497 00498 00499 /*---------------------------------------------------------------------* 00500 * Boxa sort * 00501 *---------------------------------------------------------------------*/ 00502 /*! 00503 * boxaSort() 00504 * 00505 * Input: boxa 00506 * sorttype (L_SORT_BY_X, L_SORT_BY_Y, L_SORT_BY_WIDTH, 00507 * L_SORT_BY_HEIGHT, L_SORT_BY_MIN_DIMENSION, 00508 * L_SORT_BY_MAX_DIMENSION, L_SORT_BY_PERIMETER, 00509 * L_SORT_BY_AREA, L_SORT_BY_ASPECT_RATIO) 00510 * sortorder (L_SORT_INCREASING, L_SORT_DECREASING) 00511 * &naindex (<optional return> index of sorted order into 00512 * original array) 00513 * Return: boxad (sorted version of boxas), or null on error 00514 */ 00515 BOXA * 00516 boxaSort(BOXA *boxas, 00517 l_int32 sorttype, 00518 l_int32 sortorder, 00519 NUMA **pnaindex) 00520 { 00521 l_int32 i, n, x, y, w, h, size; 00522 BOXA *boxad; 00523 NUMA *na, *naindex; 00524 00525 PROCNAME("boxaSort"); 00526 00527 if (pnaindex) *pnaindex = NULL; 00528 if (!boxas) 00529 return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); 00530 if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y && 00531 sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT && 00532 sorttype != L_SORT_BY_MIN_DIMENSION && 00533 sorttype != L_SORT_BY_MAX_DIMENSION && 00534 sorttype != L_SORT_BY_PERIMETER && 00535 sorttype != L_SORT_BY_AREA && 00536 sorttype != L_SORT_BY_ASPECT_RATIO) 00537 return (BOXA *)ERROR_PTR("invalid sort type", procName, NULL); 00538 if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) 00539 return (BOXA *)ERROR_PTR("invalid sort order", procName, NULL); 00540 00541 /* Use O(n) binsort if possible */ 00542 n = boxaGetCount(boxas); 00543 if (n > MIN_COMPS_FOR_BIN_SORT && 00544 ((sorttype == L_SORT_BY_X) || (sorttype == L_SORT_BY_Y) || 00545 (sorttype == L_SORT_BY_WIDTH) || (sorttype == L_SORT_BY_HEIGHT) || 00546 (sorttype == L_SORT_BY_PERIMETER))) 00547 return boxaBinSort(boxas, sorttype, sortorder, pnaindex); 00548 00549 /* Build up numa of specific data */ 00550 if ((na = numaCreate(n)) == NULL) 00551 return (BOXA *)ERROR_PTR("na not made", procName, NULL); 00552 for (i = 0; i < n; i++) { 00553 boxaGetBoxGeometry(boxas, i, &x, &y, &w, &h); 00554 switch (sorttype) 00555 { 00556 case L_SORT_BY_X: 00557 numaAddNumber(na, x); 00558 break; 00559 case L_SORT_BY_Y: 00560 numaAddNumber(na, y); 00561 break; 00562 case L_SORT_BY_WIDTH: 00563 numaAddNumber(na, w); 00564 break; 00565 case L_SORT_BY_HEIGHT: 00566 numaAddNumber(na, h); 00567 break; 00568 case L_SORT_BY_MIN_DIMENSION: 00569 size = L_MIN(w, h); 00570 numaAddNumber(na, size); 00571 break; 00572 case L_SORT_BY_MAX_DIMENSION: 00573 size = L_MAX(w, h); 00574 numaAddNumber(na, size); 00575 break; 00576 case L_SORT_BY_PERIMETER: 00577 size = w + h; 00578 numaAddNumber(na, size); 00579 break; 00580 case L_SORT_BY_AREA: 00581 size = w * h; 00582 numaAddNumber(na, size); 00583 break; 00584 case L_SORT_BY_ASPECT_RATIO: 00585 numaAddNumber(na, (l_float32)w / (l_float32)h); 00586 break; 00587 default: 00588 L_WARNING("invalid sort type", procName); 00589 } 00590 } 00591 00592 /* Get the sort index for data array */ 00593 if ((naindex = numaGetSortIndex(na, sortorder)) == NULL) 00594 return (BOXA *)ERROR_PTR("naindex not made", procName, NULL); 00595 00596 /* Build up sorted boxa using sort index */ 00597 boxad = boxaSortByIndex(boxas, naindex); 00598 00599 if (pnaindex) 00600 *pnaindex = naindex; 00601 else 00602 numaDestroy(&naindex); 00603 numaDestroy(&na); 00604 return boxad; 00605 } 00606 00607 00608 /*! 00609 * boxaBinSort() 00610 * 00611 * Input: boxa 00612 * sorttype (L_SORT_BY_X, L_SORT_BY_Y, L_SORT_BY_WIDTH, 00613 * L_SORT_BY_HEIGHT, L_SORT_BY_PERIMETER) 00614 * sortorder (L_SORT_INCREASING, L_SORT_DECREASING) 00615 * &naindex (<optional return> index of sorted order into 00616 * original array) 00617 * Return: boxad (sorted version of boxas), or null on error 00618 * 00619 * Notes: 00620 * (1) For a large number of boxes (say, greater than 1000), this 00621 * O(n) binsort is much faster than the O(nlogn) shellsort. 00622 * For 5000 components, this is over 20x faster than boxaSort(). 00623 * (2) Consequently, boxaSort() calls this function if it will 00624 * likely go much faster. 00625 */ 00626 BOXA * 00627 boxaBinSort(BOXA *boxas, 00628 l_int32 sorttype, 00629 l_int32 sortorder, 00630 NUMA **pnaindex) 00631 { 00632 l_int32 i, n, x, y, w, h; 00633 BOXA *boxad; 00634 NUMA *na, *naindex; 00635 00636 PROCNAME("boxaBinSort"); 00637 00638 if (pnaindex) *pnaindex = NULL; 00639 if (!boxas) 00640 return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); 00641 if (sorttype != L_SORT_BY_X && sorttype != L_SORT_BY_Y && 00642 sorttype != L_SORT_BY_WIDTH && sorttype != L_SORT_BY_HEIGHT && 00643 sorttype != L_SORT_BY_PERIMETER) 00644 return (BOXA *)ERROR_PTR("invalid sort type", procName, NULL); 00645 if (sortorder != L_SORT_INCREASING && sortorder != L_SORT_DECREASING) 00646 return (BOXA *)ERROR_PTR("invalid sort order", procName, NULL); 00647 00648 /* Generate Numa of appropriate box dimensions */ 00649 n = boxaGetCount(boxas); 00650 if ((na = numaCreate(n)) == NULL) 00651 return (BOXA *)ERROR_PTR("na not made", procName, NULL); 00652 for (i = 0; i < n; i++) { 00653 boxaGetBoxGeometry(boxas, i, &x, &y, &w, &h); 00654 switch (sorttype) 00655 { 00656 case L_SORT_BY_X: 00657 numaAddNumber(na, x); 00658 break; 00659 case L_SORT_BY_Y: 00660 numaAddNumber(na, y); 00661 break; 00662 case L_SORT_BY_WIDTH: 00663 numaAddNumber(na, w); 00664 break; 00665 case L_SORT_BY_HEIGHT: 00666 numaAddNumber(na, h); 00667 break; 00668 case L_SORT_BY_PERIMETER: 00669 numaAddNumber(na, w + h); 00670 break; 00671 default: 00672 L_WARNING("invalid sort type", procName); 00673 } 00674 } 00675 00676 /* Get the sort index for data array */ 00677 if ((naindex = numaGetBinSortIndex(na, sortorder)) == NULL) 00678 return (BOXA *)ERROR_PTR("naindex not made", procName, NULL); 00679 00680 /* Build up sorted boxa using the sort index */ 00681 boxad = boxaSortByIndex(boxas, naindex); 00682 00683 if (pnaindex) 00684 *pnaindex = naindex; 00685 else 00686 numaDestroy(&naindex); 00687 numaDestroy(&na); 00688 return boxad; 00689 } 00690 00691 00692 /*! 00693 * boxaSortByIndex() 00694 * 00695 * Input: boxas 00696 * naindex (na that maps from the new boxa to the input boxa) 00697 * Return: boxad (sorted), or null on error 00698 */ 00699 BOXA * 00700 boxaSortByIndex(BOXA *boxas, 00701 NUMA *naindex) 00702 { 00703 l_int32 i, n, index; 00704 BOX *box; 00705 BOXA *boxad; 00706 00707 PROCNAME("boxaSortByIndex"); 00708 00709 if (!boxas) 00710 return (BOXA *)ERROR_PTR("boxas not defined", procName, NULL); 00711 if (!naindex) 00712 return (BOXA *)ERROR_PTR("naindex not defined", procName, NULL); 00713 00714 n = boxaGetCount(boxas); 00715 boxad = boxaCreate(n); 00716 for (i = 0; i < n; i++) { 00717 numaGetIValue(naindex, i, &index); 00718 box = boxaGetBox(boxas, index, L_COPY); 00719 boxaAddBox(boxad, box, L_INSERT); 00720 } 00721 00722 return boxad; 00723 } 00724 00725 00726 /*! 00727 * boxaSort2d() 00728 * 00729 * Input: boxas 00730 * &naa (<optional return> numaa with sorted indices 00731 * whose values are the indices of the input array) 00732 * delta1 (min overlap that permits aggregation of a box 00733 * onto a boxa of horizontally-aligned boxes; pass 1) 00734 * delta2 (min overlap that permits aggregation of a box 00735 * onto a boxa of horizontally-aligned boxes; pass 2) 00736 * minh1 (components less than this height either join an 00737 * existing boxa or are set aside for pass 2) 00738 * Return: boxaa (2d sorted version of boxa), or null on error 00739 * 00740 * Notes: 00741 * (1) The final result is a sort where the 'fast scan' direction is 00742 * left to right, and the 'slow scan' direction is from top 00743 * to bottom. Each boxa in the boxaa represents a sorted set 00744 * of boxes from left to right. 00745 * (2) Two passes are used to aggregate the boxas, which can corresond 00746 * to characters or words in a line of text. In pass 1, only 00747 * taller components, which correspond to xheight or larger, 00748 * are permitted to start a new boxa, whereas in pass 2, 00749 * the remaining vertically-challenged components are allowed 00750 * to join an existing boxa or start a new one. 00751 * (3) If delta1 < 0, the first pass allows aggregation when 00752 * boxes in the same boxa do not overlap vertically. 00753 * The distance by which they can miss and still be aggregated 00754 * is the absolute value |delta1|. Similar for delta2 on 00755 * the second pass. 00756 * (4) On the first pass, any component of height less than minh1 00757 * cannot start a new boxa; it's put aside for later insertion. 00758 * (5) On the second pass, any small component that doesn't align 00759 * with an existing boxa can start a new one. 00760 * (6) This can be used to identify lines of text from 00761 * character or word bounding boxes. 00762 */ 00763 BOXAA * 00764 boxaSort2d(BOXA *boxas, 00765 NUMAA **pnaad, 00766 l_int32 delta1, 00767 l_int32 delta2, 00768 l_int32 minh1) 00769 { 00770 l_int32 i, index, h, nt, ne, n, m, ival; 00771 BOX *box; 00772 BOXA *boxa, *boxae, *boxan, *boxat1, *boxat2, *boxav, *boxavs; 00773 BOXAA *baa, *baad; 00774 NUMA *naindex, *nae, *nan, *nah, *nav, *nat1, *nat2, *nad; 00775 NUMAA *naa, *naad; 00776 00777 PROCNAME("boxaSort2d"); 00778 00779 if (pnaad) *pnaad = NULL; 00780 if (!boxas) 00781 return (BOXAA *)ERROR_PTR("boxas not defined", procName, NULL); 00782 00783 /* Sort from left to right */ 00784 if ((boxa = boxaSort(boxas, L_SORT_BY_X, L_SORT_INCREASING, &naindex)) 00785 == NULL) 00786 return (BOXAA *)ERROR_PTR("boxa not made", procName, NULL); 00787 00788 /* First pass: assign taller boxes to boxa by row */ 00789 nt = boxaGetCount(boxa); 00790 baa = boxaaCreate(0); 00791 naa = numaaCreate(0); 00792 boxae = boxaCreate(0); /* save small height boxes here */ 00793 nae = numaCreate(0); /* keep track of small height boxes */ 00794 for (i = 0; i < nt; i++) { 00795 box = boxaGetBox(boxa, i, L_CLONE); 00796 boxGetGeometry(box, NULL, NULL, NULL, &h); 00797 if (h < minh1) { /* save for 2nd pass */ 00798 boxaAddBox(boxae, box, L_INSERT); 00799 numaAddNumber(nae, i); 00800 } 00801 else { 00802 n = boxaaGetCount(baa); 00803 boxaaAlignBox(baa, box, delta1, &index); 00804 if (index < n) { /* append to an existing boxa */ 00805 boxaaAddBox(baa, index, box, L_INSERT); 00806 } 00807 else { /* doesn't align, need new boxa */ 00808 boxan = boxaCreate(0); 00809 boxaAddBox(boxan, box, L_INSERT); 00810 boxaaAddBoxa(baa, boxan, L_INSERT); 00811 nan = numaCreate(0); 00812 numaaAddNuma(naa, nan, L_INSERT); 00813 } 00814 numaGetIValue(naindex, i, &ival); 00815 numaaAddNumber(naa, index, ival); 00816 } 00817 } 00818 boxaDestroy(&boxa); 00819 numaDestroy(&naindex); 00820 00821 /* Second pass: feed in small height boxes; 00822 * TODO: this correctly, using local y position! */ 00823 ne = boxaGetCount(boxae); 00824 for (i = 0; i < ne; i++) { 00825 box = boxaGetBox(boxae, i, L_CLONE); 00826 n = boxaaGetCount(baa); 00827 boxaaAlignBox(baa, box, delta2, &index); 00828 if (index < n) { /* append to an existing boxa */ 00829 boxaaAddBox(baa, index, box, L_INSERT); 00830 } 00831 else { /* doesn't align, need new boxa */ 00832 boxan = boxaCreate(0); 00833 boxaAddBox(boxan, box, L_INSERT); 00834 boxaaAddBoxa(baa, boxan, L_INSERT); 00835 nan = numaCreate(0); 00836 numaaAddNuma(naa, nan, L_INSERT); 00837 } 00838 numaGetIValue(nae, i, &ival); /* location in original boxas */ 00839 numaaAddNumber(naa, index, ival); 00840 } 00841 00842 /* Sort each boxa in the boxaa */ 00843 m = boxaaGetCount(baa); 00844 for (i = 0; i < m; i++) { 00845 boxat1 = boxaaGetBoxa(baa, i, L_CLONE); 00846 boxat2 = boxaSort(boxat1, L_SORT_BY_X, L_SORT_INCREASING, &nah); 00847 boxaaReplaceBoxa(baa, i, boxat2); 00848 nat1 = numaaGetNuma(naa, i, L_CLONE); 00849 nat2 = numaSortByIndex(nat1, nah); 00850 numaaReplaceNuma(naa, i, nat2); 00851 boxaDestroy(&boxat1); 00852 numaDestroy(&nat1); 00853 numaDestroy(&nah); 00854 } 00855 00856 /* Sort boxa vertically within boxaa, using the first box 00857 * in each boxa. */ 00858 m = boxaaGetCount(baa); 00859 boxav = boxaCreate(m); /* holds first box in each boxa in baa */ 00860 naad = numaaCreate(m); 00861 if (pnaad) 00862 *pnaad = naad; 00863 baad = boxaaCreate(m); 00864 for (i = 0; i < m; i++) { 00865 boxat1 = boxaaGetBoxa(baa, i, L_CLONE); 00866 box = boxaGetBox(boxat1, 0, L_CLONE); 00867 boxaAddBox(boxav, box, L_INSERT); 00868 boxaDestroy(&boxat1); 00869 } 00870 boxavs = boxaSort(boxav, L_SORT_BY_Y, L_SORT_INCREASING, &nav); 00871 for (i = 0; i < m; i++) { 00872 numaGetIValue(nav, i, &index); 00873 boxa = boxaaGetBoxa(baa, index, L_CLONE); 00874 boxaaAddBoxa(baad, boxa, L_INSERT); 00875 nad = numaaGetNuma(naa, index, L_CLONE); 00876 numaaAddNuma(naad, nad, L_INSERT); 00877 } 00878 00879 /* fprintf(stderr, "box count = %d, numaa count = %d\n", nt, 00880 numaaGetNumberCount(naad)); */ 00881 00882 boxaaDestroy(&baa); 00883 boxaDestroy(&boxav); 00884 boxaDestroy(&boxavs); 00885 boxaDestroy(&boxae); 00886 numaDestroy(&nav); 00887 numaDestroy(&nae); 00888 numaaDestroy(&naa); 00889 if (!pnaad) 00890 numaaDestroy(&naad); 00891 00892 return baad; 00893 } 00894 00895 00896 /*! 00897 * boxaSort2dByIndex() 00898 * 00899 * Input: boxas 00900 * naa (numaa that maps from the new baa to the input boxa) 00901 * Return: baa (sorted boxaa), or null on error 00902 */ 00903 BOXAA * 00904 boxaSort2dByIndex(BOXA *boxas, 00905 NUMAA *naa) 00906 { 00907 l_int32 ntot, boxtot, i, j, n, nn, index; 00908 BOX *box; 00909 BOXA *boxa; 00910 BOXAA *baa; 00911 NUMA *na; 00912 00913 PROCNAME("boxaSort2dByIndex"); 00914 00915 if (!boxas) 00916 return (BOXAA *)ERROR_PTR("boxas not defined", procName, NULL); 00917 if (!naa) 00918 return (BOXAA *)ERROR_PTR("naindex not defined", procName, NULL); 00919 00920 /* Check counts */ 00921 ntot = numaaGetNumberCount(naa); 00922 boxtot = boxaGetCount(boxas); 00923 if (ntot != boxtot) 00924 return (BOXAA *)ERROR_PTR("element count mismatch", procName, NULL); 00925 00926 n = numaaGetCount(naa); 00927 baa = boxaaCreate(n); 00928 for (i = 0; i < n; i++) { 00929 na = numaaGetNuma(naa, i, L_CLONE); 00930 nn = numaGetCount(na); 00931 boxa = boxaCreate(nn); 00932 for (j = 0; j < nn; j++) { 00933 numaGetIValue(na, i, &index); 00934 box = boxaGetBox(boxas, index, L_COPY); 00935 boxaAddBox(boxa, box, L_INSERT); 00936 } 00937 boxaaAddBoxa(baa, boxa, L_INSERT); 00938 numaDestroy(&na); 00939 } 00940 00941 return baa; 00942 } 00943 00944 00945 /*---------------------------------------------------------------------* 00946 * Boxa statistics * 00947 *---------------------------------------------------------------------*/ 00948 /*! 00949 * boxaGetRankSize() 00950 * 00951 * Input: boxa 00952 * fract (use 0.0 for smallest, 1.0 for largest) 00953 * Return: box (with rank values for x, y, w, h), or null on error 00954 * or if the boxa is empty (has no valid boxes) 00955 * 00956 * Notes: 00957 * (1) This function does not assume that all boxes in the boxa are valid 00958 * (2) The four box parameters are sorted independently. To assure 00959 * that the resulting box size is sorted in increasing order: 00960 * - x and y are sorted in decreasing order 00961 * - w and h are sorted in increasing order 00962 */ 00963 BOX * 00964 boxaGetRankSize(BOXA *boxa, 00965 l_float32 fract) 00966 { 00967 l_int32 i, n, x, y, w, h; 00968 l_float32 xval, yval, wval, hval; 00969 NUMA *nax, *nay, *naw, *nah; 00970 BOX *box; 00971 00972 PROCNAME("boxaGetRankSize"); 00973 00974 if (!boxa) 00975 return (BOX *)ERROR_PTR("boxa not defined", procName, NULL); 00976 if (fract < 0.0 || fract > 1.0) 00977 return (BOX *)ERROR_PTR("fract not in [0.0 ... 1.0]", procName, NULL); 00978 if ((n = boxaGetCount(boxa)) == 0) 00979 return (BOX *)ERROR_PTR("boxa is empty", procName, NULL); 00980 00981 nax = numaCreate(n); 00982 nay = numaCreate(n); 00983 naw = numaCreate(n); 00984 nah = numaCreate(n); 00985 for (i = 0; i < n; i++) { 00986 boxaGetBoxGeometry(boxa, i, &x, &y, &w, &h); 00987 if (w == 0 || h == 0) continue; 00988 numaAddNumber(nax, x); 00989 numaAddNumber(nay, y); 00990 numaAddNumber(naw, w); 00991 numaAddNumber(nah, h); 00992 } 00993 numaGetRankValue(nax, 1.0 - fract, &xval); 00994 numaGetRankValue(nay, 1.0 - fract, &yval); 00995 numaGetRankValue(naw, fract, &wval); 00996 numaGetRankValue(nah, fract, &hval); 00997 box = boxCreate((l_int32)xval, (l_int32)yval, (l_int32)wval, (l_int32)hval); 00998 00999 numaDestroy(&nax); 01000 numaDestroy(&nay); 01001 numaDestroy(&naw); 01002 numaDestroy(&nah); 01003 return box; 01004 } 01005 01006 01007 /*! 01008 * boxaGetMedian() 01009 * 01010 * Input: boxa 01011 * Return: box (with median values for x, y, w, h), or null on error 01012 * or if the boxa is empty. 01013 * 01014 * Notes: 01015 * (1) See boxaGetRankSize() 01016 */ 01017 BOX * 01018 boxaGetMedian(BOXA *boxa) 01019 { 01020 PROCNAME("boxaGetMedian"); 01021 01022 if (!boxa) 01023 return (BOX *)ERROR_PTR("boxa not defined", procName, NULL); 01024 if (boxaGetCount(boxa) == 0) 01025 return (BOX *)ERROR_PTR("boxa is empty", procName, NULL); 01026 01027 return boxaGetRankSize(boxa, 0.5); 01028 } 01029 01030 01031 /*---------------------------------------------------------------------* 01032 * Other Boxaa functions * 01033 *---------------------------------------------------------------------*/ 01034 /*! 01035 * boxaaGetExtent() 01036 * 01037 * Input: boxaa 01038 * &w (<optional return> width) 01039 * &h (<optional return> height) 01040 * &box (<optional return>, minimum box containing all boxa 01041 * in boxaa) 01042 * Return: 0 if OK, 1 on error 01043 * 01044 * Notes: 01045 * (1) The returned w and h are the minimum size image 01046 * that would contain all boxes untranslated. 01047 */ 01048 l_int32 01049 boxaaGetExtent(BOXAA *boxaa, 01050 l_int32 *pw, 01051 l_int32 *ph, 01052 BOX **pbox) 01053 { 01054 l_int32 i, j, n, m, x, y, w, h, xmax, ymax, xmin, ymin; 01055 BOXA *boxa; 01056 01057 PROCNAME("boxaaGetExtent"); 01058 01059 if (!pw && !ph && !pbox) 01060 return ERROR_INT("no ptrs defined", procName, 1); 01061 if (pbox) *pbox = NULL; 01062 if (pw) *pw = 0; 01063 if (ph) *ph = 0; 01064 if (!boxaa) 01065 return ERROR_INT("boxaa not defined", procName, 1); 01066 01067 n = boxaaGetCount(boxaa); 01068 if (n == 0) 01069 return ERROR_INT("no boxa in boxaa", procName, 1); 01070 01071 xmax = ymax = 0; 01072 xmin = ymin = 100000000; 01073 for (i = 0; i < n; i++) { 01074 boxa = boxaaGetBoxa(boxaa, i, L_CLONE); 01075 m = boxaGetCount(boxa); 01076 for (j = 0; j < m; j++) { 01077 boxaGetBoxGeometry(boxa, j, &x, &y, &w, &h); 01078 xmin = L_MIN(xmin, x); 01079 ymin = L_MIN(ymin, y); 01080 xmax = L_MAX(xmax, x + w); 01081 ymax = L_MAX(ymax, y + h); 01082 } 01083 } 01084 if (pw) *pw = xmax; 01085 if (ph) *ph = ymax; 01086 if (pbox) 01087 *pbox = boxCreate(xmin, ymin, xmax - xmin, ymax - ymin); 01088 01089 return 0; 01090 } 01091 01092 01093 /*! 01094 * boxaaFlattenToBoxa() 01095 * 01096 * Input: boxaa 01097 * &naindex (<optional return> the boxa index in the boxaa) 01098 * copyflag (L_COPY or L_CLONE) 01099 * Return: boxa, or null on error 01100 * 01101 * Notes: 01102 * (1) This 'flattens' the boxaa to a boxa, taking the boxes in 01103 * order in the first boxa, then the second, etc. 01104 * (2) If &naindex is defined, we generate a Numa that gives, for 01105 * each box in the boxaa, the index of the boxa to which it belongs. 01106 */ 01107 BOXA * 01108 boxaaFlattenToBoxa(BOXAA *baa, 01109 NUMA **pnaindex, 01110 l_int32 copyflag) 01111 { 01112 l_int32 i, j, m, n; 01113 BOXA *boxa, *boxat; 01114 BOX *box; 01115 NUMA *naindex; 01116 01117 PROCNAME("boxaaFlattenToBoxa"); 01118 01119 if (pnaindex) *pnaindex = NULL; 01120 if (!baa) 01121 return (BOXA *)ERROR_PTR("baa not defined", procName, NULL); 01122 if (copyflag != L_COPY && copyflag != L_CLONE) 01123 return (BOXA *)ERROR_PTR("invalid copyflag", procName, NULL); 01124 if (pnaindex) { 01125 naindex = numaCreate(0); 01126 *pnaindex = naindex; 01127 } 01128 01129 n = boxaaGetCount(baa); 01130 boxa = boxaCreate(n); 01131 for (i = 0; i < n; i++) { 01132 boxat = boxaaGetBoxa(baa, i, L_CLONE); 01133 m = boxaGetCount(boxat); 01134 for (j = 0; j < m; j++) { 01135 box = boxaGetBox(boxat, j, copyflag); 01136 boxaAddBox(boxa, box, L_INSERT); 01137 if (pnaindex) 01138 numaAddNumber(naindex, i); /* save 'row' number */ 01139 } 01140 boxaDestroy(&boxat); 01141 } 01142 01143 return boxa; 01144 } 01145 01146 01147 /*! 01148 * boxaaAlignBox() 01149 * 01150 * Input: boxaa 01151 * box (to be aligned with the last of one of the boxa 01152 * in boxaa, if possible) 01153 * delta (amount by which consecutive components can miss 01154 * in overlap and still be included in the array) 01155 * &index (of boxa with best overlap, or if none match, 01156 * this is the index of the next boxa to be generated) 01157 * Return: 0 if OK, 1 on error 01158 * 01159 * Notes: 01160 * (1) This is not greedy; it finds the boxa whose last box has 01161 * the biggest overlap with the input box. 01162 */ 01163 l_int32 01164 boxaaAlignBox(BOXAA *baa, 01165 BOX *box, 01166 l_int32 delta, 01167 l_int32 *pindex) 01168 { 01169 l_int32 i, n, m, y, yt, h, ht, ovlp, maxovlp, maxindex; 01170 BOXA *boxa; 01171 01172 PROCNAME("boxaaAlignBox"); 01173 01174 if (!baa) 01175 return ERROR_INT("baa not defined", procName, 1); 01176 if (!box) 01177 return ERROR_INT("box not defined", procName, 1); 01178 if (!pindex) 01179 return ERROR_INT("&index not defined", procName, 1); 01180 01181 n = boxaaGetCount(baa); 01182 boxGetGeometry(box, NULL, &y, NULL, &h); 01183 maxovlp = -10000000; 01184 for (i = 0; i < n; i++) { 01185 boxa = boxaaGetBoxa(baa, i, L_CLONE); 01186 if ((m = boxaGetCount(boxa)) == 0) { 01187 L_WARNING("no boxes in boxa", procName); 01188 continue; 01189 } 01190 boxaGetBoxGeometry(boxa, m - 1, NULL, &yt, NULL, &ht); /* last one */ 01191 boxaDestroy(&boxa); 01192 01193 /* Overlap < 0 means the components do not overlap vertically */ 01194 if (yt >= y) 01195 ovlp = y + h - 1 - yt; 01196 else 01197 ovlp = yt + ht - 1 - y; 01198 if (ovlp > maxovlp) { 01199 maxovlp = ovlp; 01200 maxindex = i; 01201 } 01202 } 01203 01204 if (maxovlp + delta >= 0) 01205 *pindex = maxindex; 01206 else 01207 *pindex = n; 01208 return 0; 01209 } 01210 01211