Определение угла наклона с помощью opencv
У меня есть следующее изображение угла:
Я пытаюсь получить угол белого угла, то есть следующее:
Так что я прошел очевидный путь, чтобы убрать шум
- Преобразовал изображение в оттенки серого
- Добавлено немного размытия по Гауссу
- Применяемый порог
И результат был следующим:
Поскольку мне нужен угол, мне нужны были линии, составляющие этот угол, поэтому, конечно, я пробежал линии canny + hough, но здесь кроется проблема, я продолжаю получать несколько ложных линий, используя Hough, и я понятия не имею, как отфильтровать их:
Есть ли какой-нибудь известный способ получить достоверность каждой линии, чтобы я мог получить только правильные вертикальные + горизонтальные части угла?
2 ответа
Вот подход безcv::HoughLines()
: на двоичном изображении выполните функцию для обнаружения наиболее удаленного пикселя от центра изображения в направлении определенного направления (слева, справа, сверху, снизу). Эта функция возвращает следующие пиксели, нарисованные на изображении ниже красным, зеленым и синим цветом:
Left: [21, 35]
Top: [43, 0]
Right: [63, 35]
Из этих 3-х точек у вас есть несколько вариантов определения угла:
- Откажитесь от одной из точек и вычислите угол между двумя другими;
- Выясните, какая из них является центральной, и рассчитайте угол между всеми тремя из них;
Приведенный ниже исходный код определяет, какой из них ближе всего к центру изображения, и вычисляет угол из всех трех точек, используя atan2()
метод.
Выполнение примера с выводом двоичного изображения:
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;
}
Вам нужно поиграть с параметрами, переданными в функцию Hough.
Я полагаю, вы используете что-то вроде
HoughLines(dst, lines, 1, CV_PI/180, 100, 0, 0 );
Попробуйте поиграть с параметрами rho
, theta
а также threshold
Ваше разрешение может быть слишком хорошо.