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