How to determine rotation of a shape?

后端 未结 2 1604
隐瞒了意图╮
隐瞒了意图╮ 2021-01-14 07:09

I have following shape.

It may be rotated by unknown angle. I want to determine its rotation in reference to horizontal axis (so shape abov

2条回答
  •  执念已碎
    2021-01-14 07:51

    here's the simple logic of finding the center of gravity and the furthest contour point from it. It has an offset of 6 degrees for that contour, either because of the actual contour shape, or because of a slightly wrong center of gravity.

    int main(int argc, char* argv[])
    {
    
        //cv::Mat input = cv::imread("C:/StackOverflow/Input/rotatedShape1.png", cv::IMREAD_GRAYSCALE);
        cv::Mat input = cv::imread("C:/StackOverflow/Input/rotatedShape5.png", cv::IMREAD_GRAYSCALE);
        std::string outString = "C:/StackOverflow/Output/rotatedShape5.png";
    
        cv::Mat output;
        cv::cvtColor(input, output, cv::COLOR_GRAY2BGR);
    
        std::vector > contours;
        cv::findContours(input, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
    
        std::vector biggestContour;
        double biggestArea = 0;
        for (int i = 0; i < contours.size(); ++i)
        {
            double cArea = cv::contourArea(contours[i]);
            if (cArea > biggestArea)
            {
                biggestArea = cArea;
                biggestContour = contours[i];
            }
        }
    
        if (biggestContour.size() == 0)
        {
            std::cout << "error: no contour found. Press enter to quit." << std::endl;
            std::cin.get();
            return 0;
        }
    
        cv::Point2f centerOfMass(0,0);
        float invContourSize = 1.0f / biggestContour.size();
        for (int i = 0; i < biggestContour.size(); ++i)
        {
            centerOfMass = centerOfMass + (invContourSize * cv::Point2f(biggestContour[i]));
        }
    
        float furthestDist = 0;
        cv::Point2f furthestPoint = centerOfMass;
        for (int i = 0; i < biggestContour.size(); ++i)
        {
            float cDist = cv::norm(cv::Point2f(biggestContour[i]) - centerOfMass);
            if (cDist > furthestDist)
            {
                furthestDist = cDist;
                furthestPoint = biggestContour[i];
            }
        }
    
        // find points with very similar distance
        float maxDifference = 20; // magic number
        std::vector listOfFurthestPoints;
        for (int i = 0; i < biggestContour.size(); ++i)
        {
            float cDist = cv::norm(cv::Point2f(biggestContour[i]) - furthestPoint);
            if (cDist < maxDifference)
            {
                listOfFurthestPoints.push_back( biggestContour[i] );
                // render:
                cv::circle(output, biggestContour[i], 0, cv::Scalar(255, 0, 255), 0);
            }
        }
    
        cv::Point2f cogFP(0, 0);
        float invListSize = 1.0f / listOfFurthestPoints.size();
        for (int i = 0; i < listOfFurthestPoints.size(); ++i)
        {
            cogFP = cogFP + (invListSize * cv::Point2f(listOfFurthestPoints[i]));
        }
    
        std::cout << cogFP - centerOfMass << std::endl;
        float angle = acos((cogFP - centerOfMass).x / cv::norm(cogFP - centerOfMass)); // scalar product of [1,0] and point
        std::cout << angle * 180 / CV_PI << std::endl;
    
        cv::line(output, centerOfMass, cogFP, cv::Scalar(0, 255, 0), 1);
        cv::circle(output, centerOfMass, 5, cv::Scalar(0, 0, 255), 1);
        cv::circle(output, cogFP, 3, cv::Scalar(255, 0, 0), 1);
    
    
        cv::imwrite(outString, output);
        cv::imshow("input", input);
        cv::imshow("output", output);
        cv::waitKey(0);
        return 0;
    }
    

    this is the ouput for several rotations:

    I would love to try the circle method, using RANSAC to find the best 2 circles, but maybe won't have the time...

    Another way could be to find the turning points of the smoothed contour.

提交回复
热议问题