// Computes the center of mass and second moments for the old baseline and // 2nd moment normalizations. Returns the outline length. // The input denorm should be the normalizations that have been applied from // the image to the current state of this TBLOB. int TBLOB::ComputeMoments(FCOORD* center, FCOORD* second_moments) const { // Compute 1st and 2nd moments of the original outline. LLSQ accumulator; TBOX box = bounding_box(); // Iterate the outlines, accumulating edges relative the box.botleft(). CollectEdges(box, NULL, &accumulator, NULL, NULL); *center = accumulator.mean_point() + box.botleft(); // The 2nd moments are just the standard deviation of the point positions. double x2nd = sqrt(accumulator.x_variance()); double y2nd = sqrt(accumulator.y_variance()); if (x2nd < 1.0) x2nd = 1.0; if (y2nd < 1.0) y2nd = 1.0; second_moments->set_x(x2nd); second_moments->set_y(y2nd); return accumulator.count(); }
// Helper returns the mean direction vector from the given stats. Use the // mean direction from dirs if there is information available, otherwise, use // the fit_vector from point_diffs. static FCOORD MeanDirectionVector(const LLSQ& point_diffs, const LLSQ& dirs, const FCOORD& start_pt, const FCOORD& end_pt) { FCOORD fit_vector; if (dirs.count() > 0) { // There were directions, so use them. To avoid wrap-around problems, we // have 2 accumulators in dirs: x for normal directions and y for // directions offset by 128. We will use the one with the least variance. FCOORD mean_pt = dirs.mean_point(); double mean_dir = 0.0; if (dirs.x_variance() <= dirs.y_variance()) { mean_dir = mean_pt.x(); } else { mean_dir = mean_pt.y() + 128; } fit_vector.from_direction(Modulo(IntCastRounded(mean_dir), 256)); } else { // There were no directions, so we rely on the vector_fit to the points. // Since the vector_fit is 180 degrees ambiguous, we align with the // supplied feature_dir by making the scalar product non-negative. FCOORD feature_dir(end_pt - start_pt); fit_vector = point_diffs.vector_fit(); if (fit_vector.x() == 0.0f && fit_vector.y() == 0.0f) { // There was only a single point. Use feature_dir directly. fit_vector = feature_dir; } else { // Sometimes the least mean squares fit is wrong, due to the small sample // of points and scaling. Use a 90 degree rotated vector if that matches // feature_dir better. FCOORD fit_vector2 = !fit_vector; // The fit_vector is 180 degrees ambiguous, so resolve the ambiguity by // insisting that the scalar product with the feature_dir should be +ve. if (fit_vector % feature_dir < 0.0) fit_vector = -fit_vector; if (fit_vector2 % feature_dir < 0.0) fit_vector2 = -fit_vector2; // Even though fit_vector2 has a higher mean squared error, it might be // a better fit, so use it if the dot product with feature_dir is bigger. if (fit_vector2 % feature_dir > fit_vector % feature_dir) fit_vector = fit_vector2; } } return fit_vector; }