使用opencv检测角的角度
Detecting the angle of a corner using opencv
我有下面的一个角图片:
我正在尝试获取白角的角度,即:
所以我通过明显的路线去除噪音
- 已将图像转换为灰度图像
- 添加了一些高斯模糊
- 应用阈值
结果如下:
因为我想要角度,所以我需要构成这个角的线,所以,当然,我 运行 canny + hough 线,但问题就在这里,我不断得到多条假线使用 Hough,我不知道如何过滤掉它们:
是否有任何已知的方法来获得每条线的置信度,以便我只能获得角的正确垂直+水平部分?
您需要使用传递给 Hough 函数的参数。
我假设你使用类似
的东西
HoughLines(dst, lines, 1, CV_PI/180, 100, 0, 0 );
尝试使用参数 rho
、theta
和 threshold
,您的分辨率可能太精细了。
这里有一个没有cv::HoughLines()
的方法:在二值图像上,执行一个函数来检测从图像中心到特定方向最远的像素(左、右、上、下)。此函数 returns 以下像素,在下图中以红色、绿色和蓝色绘制:
Left: [21, 35]
Top: [43, 0]
Right: [63, 35]
根据这 3 个点,您有几个选项可以检测角度:
- 丢弃其中一个点并计算另外两个点之间的角度;
- 找出哪一个是中心点并计算所有 3 个中心点之间的角度;
下面的源代码找出哪一个最接近图像的中心,并使用 atan2()
方法计算所有 3 个点的角度。
运行 二进制图像输出示例:
Angle: 57.8477 (degrees)
似乎合法!
从这里开始,您可以进行一些改进以提高角度的准确性。其中之一是对二进制图像进行骨架化并使这些线条非常细。
源代码:
#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;
}
我有下面的一个角图片:
我正在尝试获取白角的角度,即:
所以我通过明显的路线去除噪音
- 已将图像转换为灰度图像
- 添加了一些高斯模糊
- 应用阈值
结果如下:
因为我想要角度,所以我需要构成这个角的线,所以,当然,我 运行 canny + hough 线,但问题就在这里,我不断得到多条假线使用 Hough,我不知道如何过滤掉它们:
是否有任何已知的方法来获得每条线的置信度,以便我只能获得角的正确垂直+水平部分?
您需要使用传递给 Hough 函数的参数。
我假设你使用类似
的东西HoughLines(dst, lines, 1, CV_PI/180, 100, 0, 0 );
尝试使用参数 rho
、theta
和 threshold
,您的分辨率可能太精细了。
这里有一个没有cv::HoughLines()
的方法:在二值图像上,执行一个函数来检测从图像中心到特定方向最远的像素(左、右、上、下)。此函数 returns 以下像素,在下图中以红色、绿色和蓝色绘制:
Left: [21, 35]
Top: [43, 0]
Right: [63, 35]
根据这 3 个点,您有几个选项可以检测角度:
- 丢弃其中一个点并计算另外两个点之间的角度;
- 找出哪一个是中心点并计算所有 3 个中心点之间的角度;
下面的源代码找出哪一个最接近图像的中心,并使用 atan2()
方法计算所有 3 个点的角度。
运行 二进制图像输出示例:
Angle: 57.8477 (degrees)
似乎合法!
从这里开始,您可以进行一些改进以提高角度的准确性。其中之一是对二进制图像进行骨架化并使这些线条非常细。
源代码:
#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;
}