Finds a vector corresponding to a set of vertically aligned blob edges running through the given box. The type of vector returned and the search parameters are determined by the AlignedBlobParams. vertical_x and y are updated with an estimate of the real vertical direction. (skew finding.) Returns NULL if no decent vector can be found.
| 241 | // vertical direction. (skew finding.) |
| 242 | // Returns NULL if no decent vector can be found. |
| 243 | TabVector* AlignedBlob::FindVerticalAlignment(AlignedBlobParams align_params, |
| 244 | BLOBNBOX* bbox, |
| 245 | int* vertical_x, |
| 246 | int* vertical_y) { |
| 247 | int ext_start_y, ext_end_y; |
| 248 | BLOBNBOX_CLIST good_points; |
| 249 | // Search up and then down from the starting bbox. |
| 250 | TBOX box = bbox->bounding_box(); |
| 251 | bool debug = WithinTestRegion(2, box.left(), box.bottom()); |
| 252 | int pt_count = AlignTabs(align_params, false, bbox, &good_points, &ext_end_y); |
| 253 | pt_count += AlignTabs(align_params, true, bbox, &good_points, &ext_start_y); |
| 254 | BLOBNBOX_C_IT it(&good_points); |
| 255 | it.move_to_last(); |
| 256 | box = it.data()->bounding_box(); |
| 257 | int end_y = box.top(); |
| 258 | int end_x = align_params.right_tab ? box.right() : box.left(); |
| 259 | it.move_to_first(); |
| 260 | box = it.data()->bounding_box(); |
| 261 | int start_x = align_params.right_tab ? box.right() : box.left(); |
| 262 | int start_y = box.bottom(); |
| 263 | // Acceptable tab vectors must have a mininum number of points, |
| 264 | // have a minimum acceptable length, and have a minimum gradient. |
| 265 | // The gradient corresponds to the skew angle. |
| 266 | // Ragged tabs don't need to satisfy the gradient condition, as they |
| 267 | // will always end up parallel to the vertical direction. |
| 268 | bool at_least_2_crossings = AtLeast2LineCrossings(&good_points); |
| 269 | if ((pt_count >= align_params.min_points && |
| 270 | end_y - start_y >= align_params.min_length && |
| 271 | (align_params.ragged || |
| 272 | end_y - start_y >= abs(end_x - start_x) * kMinTabGradient)) || |
| 273 | at_least_2_crossings) { |
| 274 | int confirmed_points = 0; |
| 275 | // Count existing confirmed points to see if vector is acceptable. |
| 276 | for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { |
| 277 | bbox = it.data(); |
| 278 | if (align_params.right_tab) { |
| 279 | if (bbox->right_tab_type() == align_params.confirmed_type) |
| 280 | ++confirmed_points; |
| 281 | } else { |
| 282 | if (bbox->left_tab_type() == align_params.confirmed_type) |
| 283 | ++confirmed_points; |
| 284 | } |
| 285 | } |
| 286 | // Ragged vectors are not allowed to use too many already used points. |
| 287 | if (!align_params.ragged || |
| 288 | confirmed_points + confirmed_points < pt_count) { |
| 289 | const TBOX& box = bbox->bounding_box(); |
| 290 | if (debug) { |
| 291 | tprintf("Confirming tab vector of %d pts starting at %d,%d\n", |
| 292 | pt_count, box.left(), box.bottom()); |
| 293 | } |
| 294 | // Flag all the aligned neighbours as confirmed . |
| 295 | for (it.mark_cycle_pt(); !it.cycled_list(); it.forward()) { |
| 296 | bbox = it.data(); |
| 297 | if (align_params.right_tab) { |
| 298 | bbox->set_right_tab_type(align_params.confirmed_type); |
| 299 | } else { |
| 300 | bbox->set_left_tab_type(align_params.confirmed_type); |
no test coverage detected