opencv(十五)-hough变换

1.hough线检测

原理见博客:https://blog.csdn.net/u010429424/article/details/77822783

 cv::HoughLines(InputArray src, // 输入图像,必须8-bit的灰度图像
                OutputArray lines, // 输出的极坐标来表示直线,经过调用HoughLines函数后储存了霍夫线变换检测到线条的输出矢量。每一条线由具有两个元素的矢量(ρ,θ)表示,其中,ρ是离坐标原点((0,0)(也就是图像的左上角)的距离。 θ是弧度线条旋转角度(0~ 垂直线,π/2~水平线)。
                double rho, // 生成极坐标时候的像素扫描步长,一般取值为 1 ,不要大于图像尺寸的一半
                double theta, //生成极坐标时候的角度步长,一般取值CV_PI/180,即表示一度
                int threshold, // 阈值,只有获得足够交点的极坐标点才被看成是直线
                double srn=0;// 是否应用多尺度的霍夫变换,如果不是设置0表示经典霍夫变换,多尺度表示的是使用图像金字塔,即多尺度图上进行霍夫变换
                double stn=0;//是否应用多尺度的霍夫变换,如果不是设置0表示经典霍夫变换
                double min_theta=0; // 表示角度扫描范围 0 ~180之间, 默认即可
                double max_theta=CV_PI
                  ) // 一般情况是有经验的开发者使用,需要自己反变换到平面空间
#include <iostream>
#include <opencv2\opencv.hpp>
#include <opencv2\imgproc\imgproc.hpp>

using namespace cv;
using namespace std;

int main()
{
	Mat src = imread("images/sudoku.png");
	if (src.empty()) {
		printf("could not load image...\n");
		return -1;
	}
	namedWindow("input", WINDOW_AUTOSIZE);
	imshow("input", src);
	// 去噪声与二值化
	Mat gray, binary;
	Canny(src, binary, 80, 160, 3, false);
	imshow("binary", binary);
	imwrite("D:/binary.png", binary);
    // 定义矢量结构存放检测出来的直线
	vector<Vec2f> lines;
	//通过这个函数,我们就可以得到检测出来的直线集合了
	HoughLines(midImage, lines, 1, CV_PI / 180, 150, 0, 0);
	//这里注意第五个参数,表示阈值,阈值越大,表明检测的越精准,速度越快,得到的直线越少(得到的直线都是很有把握的直线)
	//这里得到的lines是包含rho和theta的,而不包括直线上的点,所以下面需要根据得到的rho和theta来建立一条直线

	//依次画出每条线段
	for (size_t i = 0; i < lines.size(); i++)
	{
		float rho = lines[i][0]; //就是圆的半径r
		float theta = lines[i][1]; //就是直线的角度
		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(dstImage, pt1, pt2, Scalar(55, 100, 195), 1, LINE_AA); //Scalar函数用于调节线段颜色,就是你想检测到的线段显示的是什么颜色

		imshow("边缘检测后的图", midImage);
		imshow("最终效果图", dstImage);
	}
	waitKey();
	return 0;
}


cv::HoughLinesP(
InputArray src, // 输入图像,必须8-bit的灰度图像
OutputArray lines, // 输出的极坐标来表示直线,经过调用HoughLinesP函数后后存储了检测到的线条的输出矢量,每一条线由具有四个元素的矢量(x_1,y_1, x_2, y_2) 表示,其中,(x_1, y_1)和(x_2, y_2) 是是每个检测到的线段的结束点。
double rho, // 生成极坐标时候的像素扫描步长,一般取值为 1
double theta, //生成极坐标时候的角度步长,一般取值CV_PI/180,即表示一度
int threshold, // 阈值,只有获得足够交点的极坐标点才被看成是直线
double minLineLength=0;// 最小直线长度,有默认值0,表示最低线段的长度,比这个设定参数短的线段就不能被显现出来。
double maxLineGap=0;// 最大间隔,有默认值0,允许将同一行点与点之间连接起来的最大的距离。
)
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, const char *argv[])
{
	Mat src = imread("images/sudoku.png");
	if (src.empty()) {
		printf("could not load image...\n");
		return -1;
	}
	namedWindow("input", WINDOW_AUTOSIZE);
	imshow("input", src);


	// 去噪声与二值化
	Mat gray, binary;
	Canny(src, binary, 80, 160, 3, false);
	imshow("binary", binary);
	imwrite("D:/binary.png", binary);

	vector<Vec4i> lines;
	//'1'生成极坐标时候的像素扫描步长,'CV_PI/180'生成极坐标时候的角度步长,'80'判断线条是否属于直线的阈值,最小直线长度,'30'返回线段的最小长度,'10'共线线段之间的最小间隔,可以实现断线连接
	HoughLinesP(binary, lines, 1, CV_PI / 180, 80, 30, 10);
	Mat result = Mat::zeros(src.size(), src.type());
	for (size_t i = 0; i < lines.size(); i++)
	{
		line(result, Point(lines[i][0], lines[i][1]),
			Point(lines[i][2], lines[i][3]), Scalar(0, 255, 0), 1, 8);
	}
	imshow("contours", result);

	waitKey(0);
	return 0;

	waitKey(0);
	return 0;
}

2.hough圆检测

cv::HoughCircles(
InputArray image, // 输入图像 ,必须是8位的单通道灰度图像
OutputArray circles, // 输出结果,发现的圆信息
Int method, // 方法 - HOUGH_GRADIENT
Double dp, // dp = 1; 累加图像的分辨率。dp的取值越高,越容易检测到圆
Double mindist, // 10 最短距离-可以分辨是两个圆的,否则认为是同心圆- src_gray.rows/8
Double param1, //100, canny edge detection high threshold
Double param2, // 它表示在检测阶段圆心的累加器阈值。它越小的话,就可以检测到更多根本不存在的圆,而它越大的话,能通过检测的圆就更加接近完美的圆形了
Int minradius, // 最小半径
Int maxradius//最大半径 )

能较好的检测出圆心,但没法得出满意的半径,因此:
修改minRadius和maxRadius得出合适的半径
只得出圆心,半径通过其他算法得到

#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <math.h>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
	Mat gray;
	int dp =2; //
	int min_radius = 20;
	int max_radius = 100;
	int minDist = 10;//两个圆之间存在的最小距离
	Mat src = imread("images/coins.jpg");
	imshow("input", src);
	cvtColor(src, gray, COLOR_BGR2GRAY);
	GaussianBlur(gray, gray, Size(9, 9), 2, 2);
	vector<Vec3f> circles;
	HoughCircles(gray, circles, HOUGH_GRADIENT, dp, minDist, 100, 100, min_radius, max_radius);
	for (size_t i = 0; i < circles.size(); i++)
	{
		Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
		int radius = cvRound(circles[i][2]);
		// 绘制圆
		circle(src, center, 3, Scalar(0, 255, 0), -1, 8, 0);
		circle(src, center, radius, Scalar(0, 0, 255), 3, 8, 0);
	}
	namedWindow("circles", 1);
	imshow("circles", src);
	waitKey(0);
	return 0;
}

3.hough椭圆检测

    椭圆几何定理:设平面上有一个椭圆,点c为椭圆圆心,任取平面上一点p(不同于点c),点p距椭圆上点的最大距离一定大于点 c距椭圆上点的最大距离。
    下面介绍所采用的椭圆检测方法。其指导思想是利用上面的椭圆几何定理,通过查找距离椭圆上点最大距离最小的点来找到圆心,同时这个最小的最大距离就是椭圆的长轴长度。通过这种方法,椭圆的5个参数(椭圆圆心点横、纵坐标,长、短轴长,旋转角度)得到了3个,剩下的2个参数就可以在二维参数空间上统计了,即可以采用Hough变换检测直线的相同方法来检测椭圆了。
    算法的具体操作步骤如下:
    (1)对要处理的图像进行边缘检测,得到二值化的边缘轮廓图,将边缘图上的点存入数组A。
    (2)对二维平面上的每一点,计算与上一步所得数组A中点的距离,得到每一点距数组A 中点的最大距离,所有点中最大距离最小的点即是椭圆圆心(p,q),该最大距离即是椭圆长轴长度 a。
    (3)将数组A中每一点的数值和刚才得到的3个椭圆参数p、q、a代入椭圆方程E:

在这里插入图片描述
在二维参数空间上对参数b、θ进行统计,得到峰值超过一定阈值的一组参数即为椭圆。

    Mat grayImg = srcImg.clone();
    Mat colorImg;
 
    cvtColor(grayImg, colorImg, CV_GRAY2BGR);
    GaussianBlur(grayImg, grayImg, Size(3,3), 0, 0); //高斯平滑
    Canny(grayImg, grayImg, 100, 200, 3);            //Canny边缘检测
    namedWindow("Canny", CV_WINDOW_AUTOSIZE);
    imshow("Canny", grayImg);
    //提取轮廓
    vector<vector<Point>>  contours;
    vector<Vec4i> hierarchy;
    findContours(grayImg,contours,hierarchy,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE);
    //绘制查找到的轮廓
    //drawContours(colorImg, contours, -1, Scalar(0,255,0));
    //imshow("cour.bmp",colorImg);
    //Hough变换
    int accumulate;   // Hough空间最大累积量
    Ellipse myellipse;
    Mat elliImg(grayImg.size(),CV_8UC3,Scalar(0));
    for(int i=0;i< contours.size();i++)
    {
        myellipse.Computer_axy(contours[i],grayImg.size());
        accumulate=myellipse.hough_ellipse(contours[i]);
        //cout<<"accumulate:"<<accumulate<<endl;
        //cout<<"contours[i].size():"<<contours[i].size()<<endl;
        // 判断是否超过给定阈值,判断是否为椭圆,并通过长轴与短轴的轴长差去除干扰
        if(accumulate>=contours[i].size()*0.25 && abs(myellipse.getLongAxis()-myellipse.getShortAxis())>2.0
                && abs(myellipse.getLongAxis()-myellipse.getShortAxis())<50.0)
            elliImg=myellipse.draw_Eliipse(colorImg);
    }

参考

1.https://mp.weixin.qq.com/s/iwcXb3TsSHjpSHuAy0vbaA
2.https://blog.csdn.net/minghui_/article/details/80551849

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值