Reputation: 6553
I have the following image of a corner:
I'm trying to get the angle of the white corner, i.e. the following:
So I went through the obvious route to remove the noise
And the result was as follows:
Since I want the angle, I needed the lines that make-up this corner, so, of course, I ran canny + hough lines, but here lies the problem, I keep getting multiple false lines using Hough and I have no idea how to filter them out:
Is there's any known way to get the confidence of each line so I can only get the correct vertical+horizontal parts of the corner?
Upvotes: 0
Views: 5676
Reputation: 93410
Here is an approach without cv::HoughLines()
: on the binary image, execute a function to detect the most distant pixel from the center of the image towards a specific direction (left, right, top, bottom). This function returns the following pixels, drawn in the image below in red, green and blue:
Left: [21, 35]
Top: [43, 0]
Right: [63, 35]
From these 3 points you have a couple of options to detect the angle:
The source code below figures out which one is closest to the center of the image and computes the angle from all the 3 points using the atan2()
method.
Running the example with the binary image outputs:
Angle: 57.8477 (degrees)
Seems legit!
There's a few improvements you can do from here on to improve the accuracy of the angle. One of them is to skeletonize the binary image and make those lines really thin.
Source code:
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
enum imgEdge {
LEFT = 0,
RIGHT = 1,
TOP = 2,
BOTTOM = 3
};
// Find the most distant pixel from the center in a particular direction
cv::Point mostDistantPixel(const cv::Mat& input, const imgEdge& edge)
{
if (edge < 0 || edge > 3)
{
std::cout << "!!! mostDistantPixel() invalid side" << std::endl;
return cv::Point();
}
if (input.channels() > 1)
{
std::cout << "!!! mostDistantPixel() only single channel img is supported" << std::endl;
return cv::Point();
}
cv::Point mostDistant(-1,-1);
for (int r = 0; r < input.rows; r++)
{
for (int c = 0; c < input.cols; c++)
{
// Examine pure white pixels only (row, col)
if (input.at<uchar>(r,c) == 255)
{
switch (edge)
{
case imgEdge::LEFT:
if (c <= mostDistant.x || mostDistant == cv::Point(-1, -1))
mostDistant = cv::Point(c, r);
break;
case imgEdge::RIGHT:
if (c >= mostDistant.x || mostDistant == cv::Point(-1, -1))
mostDistant = cv::Point(c, r);
break;
case imgEdge::TOP:
if (r <= mostDistant.y || mostDistant == cv::Point(-1, -1))
mostDistant = cv::Point(c, r);
break;
case imgEdge::BOTTOM:
if (r >= mostDistant.y || mostDistant == cv::Point(-1, -1))
mostDistant = cv::Point(c, r);
break;
}
}
}
}
return mostDistant;
}
// Eucledian distance between 2 points
unsigned int distance(const cv::Point& a, const cv::Point& b)
{
return std::sqrt(std::pow(b.x-a.x, 2) + std::pow(b.y-a.y, 2));
}
// Compute the angle between 3 points (degrees)
double calcAngle(const cv::Point& center, const cv::Point& point, const cv::Point& base)
{
/* Compute the angle between the center, a point and it's base (starting point for the angle computation)
*
* %
* * * @ = center
* * @ # # = base (origin)
* * * % = point
* * From # to %, there are 90 degrees
*/
double angle = std::atan2(point.y - center.y, point.x - center.x) * 180 / 3.141592;
angle = (angle < 0) ? (360 + angle) : angle;
return (360 - angle);
}
int main()
{
cv::Mat img = cv::imread("corner.png", CV_LOAD_IMAGE_GRAYSCALE);
if (img.empty())
{
std::cout << "!!! imread()" << std::endl;
return -1;
}
// Find the left-most, right-most and top-most pixel
cv::Point leftPix = mostDistantPixel(img, imgEdge::LEFT);
std::cout << "Left: " << leftPix << std::endl;
cv::Point topPix = mostDistantPixel(img, imgEdge::TOP);
std::cout << "Top: " << topPix << std::endl;
cv::Point rightPix = mostDistantPixel(img, imgEdge::RIGHT);
std::cout << "Right: " << rightPix << std::endl;
// Draw pixels for debugging purposes
// cv::Mat colored;
// cv::cvtColor(img, colored, CV_GRAY2BGR);
// cv::circle(colored, leftPix, 2, cv::Scalar(0, 0, 255), cv::FILLED); // red
// cv::circle(colored, topPix, 2, cv::Scalar(0, 255, 0), cv::FILLED); // green
// cv::circle(colored, rightPix, 2, cv::Scalar(255, 0, 0), cv::FILLED); // blue
// cv::imwrite("points.png", colored);
// Find the pivot point: which of them is closest to the center
cv::Point center(img.cols/2, img.rows/2);
unsigned int leftDistance = distance(center, leftPix);
unsigned int rightDistance = distance(center, rightPix);
unsigned int topDistance = distance(center, topPix);
// This part needs a lot more testing and refinement
double angle = 0;
if (leftDistance <= rightDistance && leftDistance <= topDistance)
angle = calcAngle(leftPix, topPix, rightPix); // looks good
else if (rightDistance <= leftDistance && rightDistance <= topDistance)
angle = calcAngle(rightPix, leftPix, topPix); // needs testing
else
angle = calcAngle(topPix, leftPix, rightPix); // needs testing
std::cout << "Angle: " << angle << " (degrees)" << std::endl;
return 0;
}
Upvotes: 4
Reputation: 7121
You need to play with the parameters passed to the Hough function.
I assume you use something like
HoughLines(dst, lines, 1, CV_PI/180, 100, 0, 0 );
Try to play with the parameters rho
, theta
and threshold
, your resolution could be too fine.
Upvotes: 0