【学习opencv】实现霍夫变换(1)检测直线

目前想对于霍夫圆检测进行修改,想法是若能在固定圆心的横坐标的情景下去搜索圆,若要实现就需要对霍夫检测有一定的深入了解。

霍夫变换原理

霍夫变换原理实则就是参数空间的转变。

极坐标转换

首先因为直角坐标系中垂直于x轴的直线不存在,即转换用极坐标表示,即用 ρ,θ ρ , θ 表示,对应于下图中的rho,degree。下图中x,y轴的画法是根据图像在窗口中显示的坐标方法。

这里写图片描述

ρ,θ ρ , θ 表示的直线应该为:

y=cosθsinθx+ρsinθ y = − c o s θ s i n θ x + ρ s i n θ

推导方法:根据 ρ ρ 是过坐标原点向已知直线做的垂线段,再根据角度,求取经过x,y轴的坐标点,然后根据两点之间的坐标公式求得。

参数空间的转换

将x,y的参数空间转化为 ρ,θ ρ , θ 空间,这样做的目的是因为,将直角坐标的许多点(x,y),如若映射到 ρ,θ ρ , θ 空间,会映射成许多正弦曲线,而其中许多曲线若相交于点( ρ1,θ1 ρ 1 , θ 1 ),说明直角坐标系中许多个点共这一个( ρ1,θ1 ρ 1 , θ 1 ),即找到一条直线。

找出 ρ,θ ρ , θ 参数空间的最值

这里找出的最值应该事区域极值,但前期为了方便测试原理,只是简单在 ρ,θ ρ , θ 矩阵中寻找最大值。将找到的最值转换为斜率截距并显示在图片中

注意点

1.需要注意的是霍夫变换输入的图片是二值化的图片,即若图片某点像素值为255,则代表该点存在,即可转换到 ρ,θ ρ , θ 空间
2.canny边缘检测的图片更容易检测出尖锐的边缘部分,更容易找到直线,而如果采用阈值函数二值化的话,直线上某些点可能会被设定的阈值过滤掉。

编程实现

#include<iostream>
#include<opencv2\opencv.hpp>
using namespace cv;
using namespace std;
#define Blue Scalar(255,0,0)
//角度转弧度  π / 180×角度      弧度变角度  180 / π×弧度
#define Pi 3.1415926
void paint(Mat img, double k, double b)
{
    for (int x = 0; x < img.cols; x++)
    {
        double y = k * x + b;
        if (round(y) >= 0 && round(y) <= img.rows)
        {
            circle(img, Point(x, y), 1, Blue, 1, 8);
        }
    }
}
int main()
{
    Mat img = imread("test.jpg");
    Mat gray;
    cvtColor(img,gray,CV_BGR2GRAY);
    GaussianBlur(gray,gray,Size(5,5),1.6);
    namedWindow("img",CV_WINDOW_NORMAL);
    namedWindow("gray", CV_WINDOW_NORMAL);
    //threshold(img,img,80,255,CV_THRESH_BINARY);
    //adaptiveThreshold(gray,gray,255, ADAPTIVE_THRESH_MEAN_C,CV_THRESH_BINARY,3,3);
    Canny(gray,gray,100,200);//canny参数

    double MaxR = sqrt(gray.cols*gray.cols+gray.rows*gray.rows);//距离范围应该为[0,√]
    double MaxDegree = 180;//角度范围为0-180
    double interVal = 0.5;//间距为0.5


    //floor向下取整,round向上取整

    vector<vector<int>>A(round(MaxR),vector<int>(MaxDegree));

    uchar *data =gray.data;
    int step =gray.step[0];
    for (int x = 0; x< gray.cols;x++)
    {
        for (int y = 0; y < gray.rows; y++)
        {
            if ((int)data[y*step+x] == 255)
            {//遍历所有存在的点
                for (double degree =0; degree <MaxDegree; degree++)
                {//遍历所有斜率k的取值
                    double dis = x*cos((Pi*degree)/180.0)+y*sin((Pi*degree) / 180.0);
                    if (dis >= MaxR || dis <0)continue;
                    A[round(dis)][degree]++;
                }
            }
        }
    }

    cout << "test1" << endl;
    //寻找累加器矩阵中的极值点
    int max = 0,degree = 0, dis = 0;
    for (int i = 0; i < A.size(); i++)
    {
        for (int j = 0; j < A[0].size(); j++)
        {
            if (A[i][j] > max) { max = A[i][j]; dis= i; degree = j; }
        }
    }
    //cout << "test2" << endl;

    cout << "找到的角度" << degree << endl;
    cout << "距离:" << dis << endl;
    double k = -cos((Pi*degree) / 180.0) / sin((Pi*degree) / 180.0);//若用tan,考虑什么时候为0
    double b = dis / sin((Pi*degree) / 180.0);
    cout << "斜率:" << k << endl;
    cout << "截距:" << b << endl;

    //画出找到的曲线
    paint(img, k,b);
    imshow("img",img);
    imshow("gray",gray);

    waitKey(0);
    destroyAllWindows();

    return 0;
}

需要完善的地方很多!例如求八邻域极值,避免重复检测等,后期还需要了解如何实现霍夫圆检测。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值