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
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.