Extracts Tesseract features and appends them to the features vector. Startpt to lastpt, inclusive, MUST have the same src_outline member, which may be NULL. The vector from lastpt to its next is included in the feature extraction. Hidden edges should be excluded by the caller. If force_poly is true, the features will be extracted from the polygonal approximation even if more accurate data is avail
| 327 | // If force_poly is true, the features will be extracted from the polygonal |
| 328 | // approximation even if more accurate data is available. |
| 329 | static void ExtractFeaturesFromRun( |
| 330 | const EDGEPT* startpt, const EDGEPT* lastpt, |
| 331 | const DENORM& denorm, double feature_length, bool force_poly, |
| 332 | GenericVector<INT_FEATURE_STRUCT>* features) { |
| 333 | const EDGEPT* endpt = lastpt->next; |
| 334 | const C_OUTLINE* outline = startpt->src_outline; |
| 335 | if (outline != NULL && !force_poly) { |
| 336 | // Detailed information is available. We have to normalize only from |
| 337 | // the root_denorm to denorm. |
| 338 | const DENORM* root_denorm = denorm.RootDenorm(); |
| 339 | int total_features = 0; |
| 340 | // Get the features from the outline. |
| 341 | int step_length = outline->pathlength(); |
| 342 | int start_index = startpt->start_step; |
| 343 | // pos is the integer coordinates of the binary image steps. |
| 344 | ICOORD pos = outline->position_at_index(start_index); |
| 345 | // We use an end_index that allows us to use a positive increment, but that |
| 346 | // may be beyond the bounds of the outline steps/ due to wrap-around, to |
| 347 | // so we use % step_length everywhere, except for start_index. |
| 348 | int end_index = lastpt->start_step + lastpt->step_count; |
| 349 | if (end_index <= start_index) |
| 350 | end_index += step_length; |
| 351 | LLSQ prev_points; |
| 352 | LLSQ prev_dirs; |
| 353 | FCOORD prev_normed_pos = outline->sub_pixel_pos_at_index(pos, start_index); |
| 354 | denorm.NormTransform(root_denorm, prev_normed_pos, &prev_normed_pos); |
| 355 | LLSQ points; |
| 356 | LLSQ dirs; |
| 357 | FCOORD normed_pos; |
| 358 | int index = GatherPoints(outline, feature_length, denorm, root_denorm, |
| 359 | start_index, end_index, &pos, &normed_pos, |
| 360 | &points, &dirs); |
| 361 | while (index <= end_index) { |
| 362 | // At each iteration we nominally have 3 accumulated sets of points and |
| 363 | // dirs: prev_points/dirs, points/dirs, next_points/dirs and sum them |
| 364 | // into sum_points/dirs, but we don't necessarily get any features out, |
| 365 | // so if that is the case, we keep accumulating instead of rotating the |
| 366 | // accumulators. |
| 367 | LLSQ next_points; |
| 368 | LLSQ next_dirs; |
| 369 | FCOORD next_normed_pos; |
| 370 | index = GatherPoints(outline, feature_length, denorm, root_denorm, |
| 371 | index, end_index, &pos, &next_normed_pos, |
| 372 | &next_points, &next_dirs); |
| 373 | LLSQ sum_points(prev_points); |
| 374 | // TODO(rays) find out why it is better to use just dirs and next_dirs |
| 375 | // in sum_dirs, instead of using prev_dirs as well. |
| 376 | LLSQ sum_dirs(dirs); |
| 377 | sum_points.add(points); |
| 378 | sum_points.add(next_points); |
| 379 | sum_dirs.add(next_dirs); |
| 380 | bool made_features = false; |
| 381 | // If we have some points, we can try making some features. |
| 382 | if (sum_points.count() > 0) { |
| 383 | // We have gone far enough from the start. Make a feature and restart. |
| 384 | FCOORD fit_pt = sum_points.mean_point(); |
| 385 | FCOORD fit_vector = MeanDirectionVector(sum_points, sum_dirs, |
| 386 | prev_normed_pos, normed_pos); |
no test coverage detected