使用opencv检测角的角度

Detecting the angle of a corner using opencv

我有下面的一个角图片:

我正在尝试获取白角的角度,即:

所以我通过明显的路线去除噪音

  1. 已将图像转换为灰度图像
  2. 添加了一些高斯模糊
  3. 应用阈值

结果如下:

因为我想要角度,所以我需要构成这个角的线,所以,当然,我 运行 canny + hough 线,但问题就在这里,我不断得到多条假线使用 Hough,我不知道如何过滤掉它们:

是否有任何已知的方法来获得每条线的置信度,以便我只能获得角的正确垂直+水平部分?

您需要使用传递给 Hough 函数的参数。

我假设你使用类似

的东西
HoughLines(dst, lines, 1, CV_PI/180, 100, 0, 0 );

尝试使用参数 rhothetathreshold,您的分辨率可能太精细了。

Hough OpenCV

Hough Matlab

这里有一个没有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;
}