RecognitionResult GeometricRecognizer::recognize(Path2D points) { //--- Make sure we have some templates to compare this to //--- or else recognition will be impossible if (points.size() < 5){ return RecognitionResult("Unknown", NULL); } if (templates.empty()) { std::cout << "No templates loaded so no symbols to match." << std::endl; return RecognitionResult("Unknown", NULL); } points = normalizePath(points); //--- Initialize best distance to the largest possible number //--- That way everything will be better than that double bestDistance = MAX_DOUBLE; //--- We haven't found a good match yet int indexOfBestMatch = -1; //--- Check the shape passed in against every shape in our database for (int i = 0; i < (int)templates.size(); i++) { //--- Calculate the total distance of each point in the passed in //--- shape against the corresponding point in the template //--- We'll rotate the shape a few degrees in each direction to //--- see if that produces a better match double distance = distanceAtBestAngle(points, templates[i]); if (distance < bestDistance) { bestDistance = distance; indexOfBestMatch = i; } } //--- Turn the distance into a percentage by dividing it by //--- half the maximum possible distance (across the diagonal //--- of the square we scaled everything too) //--- Distance = hwo different they are //--- Subtract that from 1 (100%) to get the similarity double score = 1.0 - (bestDistance / halfDiagonal); //--- Make sure we actually found a good match //--- Sometimes we don't, like when the user doesn't draw enough points if (-1 == indexOfBestMatch) { //cout << "Couldn't find a good match." << endl; return RecognitionResult("Unknown", 1); } cout<<score<<endl; RecognitionResult bestMatch(templates[indexOfBestMatch].name, score); return bestMatch; };
void ShapeDetector::report( const std::string &i_name, const std::vector<CGPoint> &i_path ) { Path2D p; for ( auto pt : i_path ) { if ( p.size() == 0 ) p.move_to( pt ); else p.line_to( pt ); } shapeDetected( i_name, p ); }
Point2D GeometricRecognizer::centroid(Path2D points) { double x = 0.0, y = 0.0; for (Path2DIterator i = points.begin(); i != points.end(); i++) { Point2D point = *i; x += point.x; y += point.y; } x /= points.size(); y /= points.size(); return Point2D(x, y); }
std::vector<Vector2f> GeneratePathNormals(const Path2D& path, bool is_closed, bool outward) { assert(path.size() >= 2); // Here we are going to use a simple 3-point interpolation method // first we need to make the path in ccw direction ClipperLib::Path scaled_path = UScalePathDiaToClipper(path); if (!ClipperLib::Orientation(scaled_path)) { ClipperLib::ReversePath(scaled_path); } Path2D ccw_path = DScalePathClipperToDia(scaled_path); // Special treatment for the first and last point. std::vector<Vector2f> output_vecs(path.size()); if (is_closed) { output_vecs[0] = GetNormalFromTwoPoints(ccw_path[ccw_path.size() - 1], ccw_path[1]); output_vecs.back() = GetNormalFromTwoPoints(ccw_path[ccw_path.size() - 2], ccw_path[0]); } else { output_vecs[0] = GetNormalFromTwoPoints(ccw_path[0], ccw_path[1]); output_vecs.back() = GetNormalFromTwoPoints(ccw_path[ccw_path.size() - 2], ccw_path[ccw_path.size() - 1]); } // We use the point before and after ith point to interpolate its normal. for (size_t i = 1; i < ccw_path.size() - 1; ++i) { output_vecs[i] = GetNormalFromTwoPoints(ccw_path[i - 1], ccw_path[i + 1]); } // Reverse normal direction if (!outward) { for (size_t i = 0; i < ccw_path.size(); ++i) { output_vecs[i] = -output_vecs[i]; } } return output_vecs; }
vector<double> GeometricRecognizer::vectorize(Path2D points) // for Protractor { double sum = 0.0; vector<double> vectorized; // Preprocessing, Move from .x.y notation to 1D vector notation // points[i](.x,.y) => vectorized(i, i+1, ...) for (unsigned int i = 0; i < points.size(); i++) { vectorized.push_back(points[i].x); vectorized.push_back(points[i].y); sum += points[i].x * points[i].x + points[i].y * points[i].y; } // normalize values : dividing by magnitude // magnitude = sqrt ( sum_i(x²) + sum_i(y²) ) double magnitude = sqrt(sum); for (unsigned int i = 0; i < vectorized.size(); i++) vectorized[i] /= magnitude; return vectorized; }
std::vector<Vector2f> SimplifyPolyline(const Path2D& path, float rel_tol) { if (path.size() <= 2) { return path; } std::vector<Vector2f> out; if (true) { // we compute the boundary square size of the path AABB bound = GetAABBWithPadding(path, 0); Vector2f span = bound.upper_bound - bound.lower_bound; // set a tolerance to remove points that are too close to // each other. the relative tolerance is 0.5% of max size // so the simplified curve is still pretty smooth float tol = std::max(span(0), span(1)) * rel_tol; out = PolylineDouglasPeuckerIterative(path, tol); } else { // only one method implemented // the douglas_peucker method may change the topology of input curves assert(0); } return out; }