多尺度霍夫变换
作用:在图像处理中检测是否存在直线、圆形、椭圆等规则形状。
原理:
- 将图像空间x-y直角坐标系中,经过像素点(x0,y0)的直线y0=kx0+b转化为参数空间k-b坐标系的b=-kx0+y0。即霍夫变换将x-y直角坐标系中经过一点的无数条直线映射成了k-b坐标的一条直线l1,直线上的每个点对应着x-y直角坐标系中的一条直线。
- 经过像素点(x1,y1)的所有直线在k-b空间中也有一条对应直线l2。图像空间x-y直角坐标系中,同时经过(x0,y0)和(x1,y1)的一条直线的斜率和截距,在参数空间中对应l1和l2的交点。
- A+B->通过霍夫变换寻找图像中的直线就是寻找参数空间中大量直线相交的一点。
- 在空间x-y直角坐标系中,所有像素点的x坐标相同时,直线上的像素点利用霍夫变换得到的直线是平行的,无法交于一点。于是引入极坐标表示空间x-y直角坐标系中的直线。
- 参数空间中曲线是连续的,实际上像素点是离散的,因此将轴和轴离散化,用离散后的方格表示每条正弦曲线。
步骤:
- 参数空间坐标轴离散化,如=0°,10°…,=0.1,0.2,0.3…
- 将图像中每个非零像素通过映射关系求取在参数空间通过的方格
- 统计参数空间内每个方格出现的次数,选取次数大于一定阈值的方格作为表示直线的方格
- 将参数空间中表示直线的方格的参数作为图像中直线的参数
优点:抗干扰能力强,对图像中直线的残缺部分、噪声以及其他共存的非直线结构不敏感,能容忍特征边界描述中的间隙,并且相对不受噪声影响
缺点:时间、空间复杂度高,并且检测精度受参数离散间隔制约
实际步骤:
- 边缘提取Canny()
- 二值化
- 霍夫变换函数HoughLines()检测直线
- 根据输出的极坐标形式的参数,计算出直线与经过坐标原点的垂线的交点的坐标,利用直线的线性关系计算出直线两端尽可能远的端点的坐标,绘制直线
#include<opencv2/opencv.hpp>
#include<quickopencv.h>
#include<iostream>
#include<math.h>
#include <opencv2/imgproc.hpp>
#include<vector>
using namespace cv;
using namespace std;
void drawLine(Mat& img, //要标记直线的图像
vector<Vec2f>lines, //检测的直线数据
double rows, //原图像的行数/高
double cols, //列数/宽
Scalar scalar, //绘制直线的颜色
int n //绘制直线的线宽
)
{
Point pt1, pt2;
for (size_t i = 0; i < lines.size(); i++) {
float rho = lines[i][0]; //计算直线距离坐标原点的距离
float theta = lines[i][1]; //直线与坐标原点的垂线与x轴的夹角
double a = cos(theta); //夹角的余弦
double b = sin(theta); //夹角的正弦
double x0 = a* rho, y0 = b * rho ; //直线与过坐标原点的垂线的交点
double length = max(rows,cols); //图像高宽的最大值
//计算直线上的一点
pt1.x = cvRound(x0+length*(-b));
pt1.y = cvRound(y0 + length * (a));
//计算直线上的另一点
pt2.x = cvRound(x0 - length * (-b));
pt2.y = cvRound(y0 - length * (a));
//两点绘制一条直线
line(img, pt1, pt2, scalar, n);
}
}
int main(int argc, char** argv) {
//QuickDemo qd;
//qd.myFilter_demo(src);
Mat img = imread("D:/images/dui.png", IMREAD_GRAYSCALE);
resize(img, img, Size(img.cols / 3, img.rows / 3), 0, 0, INTER_LINEAR);
if (img.empty()) {
cout << "请确认图像文件名称是否正确" << endl;
return -1;
}
Mat edge;
//检测边缘图像,并进行二值化
Canny(img, edge, 80, 180, 3, false); //边缘检测
threshold(edge, edge, 170, 255, THRESH_BINARY); //二值化
//用不同的累加器检测直线
vector<Vec2f>lines1, lines2;
HoughLines(edge, lines1, 1, CV_PI / 180, 50, 0, 0);
HoughLines(edge, lines2, 1, CV_PI / 180, 150, 0, 0);
//在原图像中绘制直线
Mat img1, img2;
img.copyTo(img1);
img.copyTo(img2);
drawLine(img1, lines1, edge.rows, edge.cols, Scalar(255), 2);
drawLine(img2, lines2, edge.rows, edge.cols, Scalar(255), 2);
//显示图像
imshow("edge", edge);
imshow("img", img);
imshow("img1", img1);
imshow("img2", img2);
waitKey(0);//此时图片显示时间为一直停留。(x)为x毫秒
//destroyAllWindows();
return 0;
}
渐进概率式霍夫变换
背景:
标准霍夫变换和多尺度霍夫变换提取直线时无法准确知道图像中直线或者线段的长度只能得到图像中是否存在符合要求的直线,以及直线的极坐标解析式。因此引入渐进概率式霍夫变换以得到图像中满足条件的直线或者线段的两个端点坐标,进而确定直线或线段的位置。
#include<opencv2/opencv.hpp>
#include<quickopencv.h>
#include<iostream>
#include<math.h>
#include <opencv2/imgproc.hpp>
#include<vector>
using namespace cv;
using namespace std;
int main(int argc, char** argv) {
//QuickDemo qd;
//qd.myFilter_demo(src);
Mat img = imread("D:/images/hough.png", IMREAD_GRAYSCALE);
//resize(img, img, Size(img.cols / 3, img.rows / 3), 0, 0, INTER_LINEAR);
if (img.empty()) {
cout << "请确认图像文件名称是否正确" << endl;
return -1;
}
Mat edge;
//检测边缘图像,并进行二值化
Canny(img, edge, 80, 180, 3, false); //边缘检测
threshold(edge, edge, 170, 255, THRESH_BINARY); //二值化
//用渐进概率式霍夫变换提取直线
vector<Vec4i>linesp1, linesp2;
HoughLinesP(edge, linesp1, 1, CV_PI / 180, 150, 30, 10);
HoughLinesP(edge, linesp2, 1, CV_PI / 180, 150, 30, 30);
//绘制两个点 连接最大距离10直线检测结果
Mat img1;
img.copyTo(img1);
for (size_t i = 0; i < linesp1.size(); i++) {
line(img1, Point(linesp1[i][0], linesp1[i][1]),
Point(linesp1[i][2], linesp1[i][3]), Scalar(255), 3);
}
//绘制两个点 连接最大距离30直线检测结果
Mat img2;
img.copyTo(img2);
for (size_t i = 0; i < linesp2.size(); i++) {
line(img2, Point(linesp1[i][0], linesp1[i][1]),
Point(linesp1[i][2], linesp1[i][3]), Scalar(255), 3);
}
//显示图像
imshow("edge", edge);
imshow("img", img);
imshow("img1", img1);
imshow("img2", img2);
waitKey(0);//此时图片显示时间为一直停留。(x)为x毫秒
//destroyAllWindows();
return 0;
}
标准霍夫变换
作用:在含有坐标的点中寻找是否存在直线
#include<opencv2/opencv.hpp>
#include<quickopencv.h>
#include<iostream>
#include<math.h>
#include <opencv2/imgproc.hpp>
#include<vector>
using namespace cv;
using namespace std;
int main(int argc, char** argv) {
//QuickDemo qd;
//qd.myFilter_demo(src);
/*
Mat img = imread("D:/images/hough.png", IMREAD_GRAYSCALE);
//resize(img, img, Size(img.cols / 3, img.rows / 3), 0, 0, INTER_LINEAR);
if (img.empty()) {
cout << "请确认图像文件名称是否正确" << endl;
return -1;
}
//显示图像
imshow("edge", edge);
imshow("img", img);
imshow("img1", img1);
imshow("img2", img2);
*/
system("color F0"); //更改输出界面的颜色
Mat lines; //存放检测直线结果的矩阵
vector<Vec3d>line3d; //换一种结果存放形式
vector<Point2f>point; //待检测是否存在直线的所有点
const static float Points[20][2] = { {0.0f,369.0f},{10.0f,364.0f},{20.0f,358.0f},{30.0f,352.0f},
{40.0f,346.0f},{50.0f,341.0f},{60.0f,335.0f},{70.0f,329.0f},
{80.0f,323.0f},{90.0f,318.0f},{100.0f,312.0f},{110.0f,306.0f},
{120.0f,300.0f},{130.0f,295.0f},{140.0f,289.0f},{150.0f,284.0f},
{160.0f,277.0f},{170.0f,271.0f},{180.0f,266.0f},{190.0f,260.0f}
};
//将所有点存放在vector中,用于输入函数
for (int i = 0; i < 20; i++) {
point.push_back(Point2f(Points[i][0], Points[i][1]));
}
//参数设置
double rhoMin = 0.0f; //最小长度
double rhoMax = 360.0f; //最大长度
double rhoStep = 1; //离散化单位距离
double thetaMin = 0.0f; //最小角度
double thetaMax = CV_PI/2.0f; //最大角度
double thetaStep = CV_PI / 180.0f; //离散化单位角度
HoughLinesPointSet(point, lines, 20, 1, rhoMin, rhoMax, rhoStep, thetaMin, thetaMax, thetaStep);
lines.copyTo(line3d);
//输出结果
for (int i = 0; i < line3d.size(); i++) {
cout << "vectes:" << (int)line3d.at(i).val[0] << ","
<< "rho:" << line3d.at(i).val[1] << ","
<< "theta:" << (int)line3d.at(i).val[2] << endl;
}
waitKey(0);//此时图片显示时间为一直停留。(x)为x毫秒
//destroyAllWindows();
return 0;
}