Определение угла наклона с помощью opencv

У меня есть следующее изображение угла:

оригинал

Я пытаюсь получить угол белого угла, то есть следующее:

ожидаемый результат

Так что я прошел очевидный путь, чтобы убрать шум

  1. Преобразовал изображение в оттенки серого
  2. Добавлено немного размытия по Гауссу
  3. Применяемый порог

И результат был следующим:

после порога

Поскольку мне нужен угол, мне нужны были линии, составляющие этот угол, поэтому, конечно, я пробежал линии canny + hough, но здесь кроется проблема, я продолжаю получать несколько ложных линий, используя Hough, и я понятия не имею, как отфильтровать их:

после Hough Transform

Есть ли какой-нибудь известный способ получить достоверность каждой линии, чтобы я мог получить только правильные вертикальные + горизонтальные части угла?

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Ваше разрешение может быть слишком хорошо.

Hough OpenCV

Хаф Матлаб

Другие вопросы по тегам