霍夫变换概述
霍夫变换(Hough Transform)是图像处理中的一种特征提取技术,该过程在一个参数空间中通过计算累计结果的局部最大值得到一个符合该特征的集合作为霍夫变换的结果。
霍夫线检测
霍夫线变换是一种寻找直线的方法,在寻找霍夫变换之前,要对图像进行边缘检测,即霍夫线的输入为二值图像。
原理
其意思就是,直线在极坐标中的表示形式为:r=x*cos(θ)+y*sin(θ)
,即每一对通过(r,θ)代表了一条通过(x,y)的直线。
如果给定一个点(8,6)则r=8*cos(θ)+6*sin(θ)
对应与一条正弦曲线。
对图像中的所有点都进行上述的操作,如果两个不同点进行操作得到的曲线在平面(θ,r)上相交,则意味正他们通过一条直线,因此我们可以通过设置直线上点的阈值来定义多少条曲线相交于一点,这要才认为检测到了一条直线。
OpenCV中霍夫变换
介绍两种霍夫线变换,标准霍夫线变换和累计概率霍夫线变换。
Standard Hough Line Transform的原型为:
void HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0 )
image: 输入图像,类型为二进制图像
lines:lines类型,存储了矢量(p,θ)
rho:以像素为单位的距离精度
theta:以弧度为单位的角度精度
threshold:阈值,即识别为直线时他在累加平面中必须达到的值
srn:默认为0
stn默认为0,关于更多,请查看官方文档
Probabilistic Hough Line Transform
累计霍夫变换是对标准霍夫变换的一种改进,他在一定的范围内进行霍夫变换,计算单独线段的方向以及范围,从而减少计算了,缩短了时间。
void HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength=0, double maxLineGap=0 )
不同点在最后的两个参数。
minLineLength:表示最低线段的长度,比此小的都不能显示出来
maxLineGap:允许同一行点与点之间连接起来的最大距离
下面对累加霍夫变换进行展示C++代码:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int thre, minLineLength, maxLineGap;
Mat src, dst;
void on_HoughLines(int, void *);
int main() {
Mat ori = imread("road.jpg");
namedWindow("dst");
imshow("origin", ori);
Canny(ori, src, 50, 200,3);
cvtColor(src, dst, CV_GRAY2BGR);
imshow("canny image", src);
thre = 80;
minLineLength = 50;
maxLineGap=10;
createTrackbar("threshold:", "dst", &thre, 300, on_HoughLines);
createTrackbar("minLine :", "dst", &minLineLength, 300, on_HoughLines);
createTrackbar("maxGap :", "dst", &maxLineGap, 20, on_HoughLines);
on_HoughLines(thre, 0);
on_HoughLines(minLineLength, 0);
on_HoughLines(maxLineGap, 0);
/*HoughLinesP(src, lines, 1, CV_PI / 180, 50, 60, 10);
for (size_t i = 0; i < lines.size(); i++) {
Vec4i l = lines[i];
line(dst, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(186,88, 255));
}*/
//imshow("dst image", dst);
waitKey(0);
}
//
void on_HoughLines(int, void *) {
vector<Vec4i> lines;
Mat dstImage = dst.clone();
Mat srcImage = src.clone();
HoughLinesP(srcImage, lines, 1, CV_PI / 180, thre, minLineLength, maxLineGap);
for (size_t i = 0; i < lines.size(); i++) {
Vec4i l = lines[i];
line(dstImage, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(186, 88, 255),2);
}
imshow("dst", dstImage);
}
可以调整参数使得检测到道路的直线段,效果如下:
原图和canny算子进行边缘检测的结果图:
直线检测效果图:
霍夫圆检测
一条直线可以以极坐标的方式进行表示(r,θ),一个圆需要三个参数(x,y,r)进行表示,通常用霍夫梯度法来解决圆变换的问题。
霍夫梯度法
霍夫梯度法的原理是首先对图像进行边缘检测,用canny,然后考虑边缘图像中的每一个非0点,考虑其局部梯度,通过Sobel函数计算x,y方向上的sobel一阶导数得到梯度,利用得到的梯度,由斜率制定直线上的每一个点都在累加器中被累加,然后从累加器中这些点钟选择候选的中心画圆。
opencv中的霍夫圆检测:
C++: void HoughCircles(InputArray image, OutputArray circles, int method, double dp, double minDist, double param1=100, double param2=100, int minRadius=0, int maxRadius=0 )
image: 输入图像,类型为二进制图像
circles:经调用后存储检测到的圆的输出矢量(x,y,radius)
method:检测的方法,位霍夫梯度法
dp:用来检测原型的累加器图像的分辨率与输入图像之比的道说,dp=1,累加器和输入图像具有相同的分辨率。
minDist:检测到的圆圆心的最小距离,即让算法能够区分不同圆直径的距离
param1:默认100,表示传递给canny边缘算子的高阈值
param2:表示在检测阶段圆心的累计其阈值,他越小,就越可以检测到更多根本不存在的预案,越大的话,检测的预案就更加接近完美圆
minRadius:表示半径的最小值
maxRadiux:表示半径的最大值
C++版本的示例:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main() {
Mat src = imread("planet_glow.jpg");
namedWindow("origin");
imshow("origin", src);
//转出灰度图,并进行平滑,因为要进行边缘检测,最好进行平滑
Mat gray;
cvtColor(src, gray, CV_BGR2GRAY);
imshow("gray", gray);;
Mat dst;
medianBlur(gray, dst, 5);;
imshow("median filter", dst);
vector<Vec3f> circles;
HoughCircles(dst, circles, HOUGH_GRADIENT,1,120,100,30,0,0);
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, 2, Scalar(0, 255, 0), 2);
//绘制轮廓
circle(src, center, radius, Scalar(0, 255, 0), 2);
}
imshow("result", src);
waitKey(0);
}
Python版本:
import cv2
import numpy as np
planets = cv2.imread('planet_glow.jpg')
gray_img = cv2.cvtColor(planets, cv2.COLOR_BGR2GRAY)
img = cv2.medianBlur(gray_img, 5)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
circles = cv2.HoughCircles(img,cv2.HOUGH_GRADIENT,1,120,
param1=100,param2=30,minRadius=0,maxRadius=0)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
# draw the outer circle
cv2.circle(planets,(i[0],i[1]),i[2],(0,255,0),2)
# draw the center of the circle
cv2.circle(planets,(i[0],i[1]),2,(0,0,255),3)
cv2.imwrite("planets_circles.jpg", planets)
cv2.imshow("HoughCirlces", planets)
cv2.waitKey()
cv2.destroyAllWindows()
结果如下所示: