OpenCV 霍夫线

官方有两个算子,HoughLines和HoughLinesP

HoughLines得到两个变量:θ、ρ

在旋转矫正时,使用的是angle。具体见下图。

θ 弧度制,[0,π)
angle 角度制,[-90°,90°),正值则逆时针旋转
下图中的angle是负值(需顺时针选择),angle=θ*180/π - 90

 

#include <iostream>
#include<opencv.hpp>
using namespace cv;

#include <numeric>   //for accumulate

int main()
{
    cv::Mat src = cv::imread("C:/line.bmp");
    //灰度化
    cv::Mat gray;
    cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY);
    //二值化,得到1和255的图
    Mat binary;
    threshold(gray, binary, 0, 255, cv::THRESH_BINARY_INV | cv::THRESH_OTSU);
    imshow("binary", binary);
    //霍夫线
    std::vector<cv::Vec2f> lines;
    cv::HoughLines(binary, lines, 1.0, CV_PI / 180, 100, 0, 0);
    int numLines = lines.size();
    std::vector<float> thetas;
    for (int l = 0; l < numLines; l++)
    {
        float rho = lines[l][0];                    //ρ
        float theta = lines[l][1];                    //θ
        cv::Point pt1, pt2;
        double a = cos(theta), b = sin(theta);
        double x0 = a * rho, y0 = b * rho;
        pt1.x = cvRound(x0 + 1000 * (-b));
        pt1.y = cvRound(y0 + 1000 * (a));
        pt2.x = cvRound(x0 - 1000 * (-b));
        pt2.y = cvRound(y0 - 1000 * (a));
        line(binary, pt1, pt2, cv::Scalar(255), 1);    //在二值图上画线

        thetas.push_back(theta);
    }
    cv::imshow("Hough直线", binary);
    //角度均值
    float sum = std::accumulate(std::begin(thetas), std::end(thetas), 0.0);
    float meanTheta = sum / thetas.size();
    float meanAngle = (meanTheta / CV_PI) * 180 - 90;
  
    //矫正       
    Point2f center;                                        //旋转中心为图像中心 
    center.x = float(src.cols / 2.0);
    center.y = float(src.rows / 2.0);
    Mat M = getRotationMatrix2D(center, meanAngle, 1);    //计算二维旋转的仿射变换矩阵    
    Mat img_rotate;
    int length = 0;
    length = sqrt(src.cols * src.cols + src.rows * src.rows);
    M.at<double>(0,2) += (length - src.size().width) / 2.0;    //平移,仿射变换矩阵2*3,第3列代表平移
    M.at<double>(1,2) += (length - src.size().height) / 2.0;
    warpAffine(src, img_rotate, M, Size(length, length), 1, 0, Scalar(255, 255, 255));//仿射变换,背景色填充为白色 
    //展示
    imshow("img_rotate", img_rotate);
    waitKey();
}

【探讨点】

θ∈[0,π),在π时会突变为0,存在临界情况。这就导致在求多条线角度平均值的时候会产生错误情况。

如下图三条线,θ均值应该为0,但是计算为π/3

目前还没有找到合理的方法求角度均值,暂时解决如下:

适用情况:线条角度偏差不大时,那么在临界左右,极差会很大,靠近π的要减去π后再求均值

    //直接计算均值,会在180°附近出现问题。以下计算方式有个假设:直线方向差异不大
    //极差超过一定值的,靠近180°的要-180°
    float max = *std::max_element(thetas.begin(), thetas.end());
    float min = *std::min_element(thetas.begin(), thetas.end());
    if ((max - min) > CV_PI / 2)
    {
        for (auto& i : thetas)            //引用,效率更高。i被改变则对应的元素被改变
        {
            if (CV_PI - i < 0.02)    //接近180°时
            {
                i -= CV_PI;
            }
        }
    }

 

posted @ 2022-02-21 11:13  夕西行  阅读(89)  评论(0编辑  收藏  举报