点击上方“小白学视觉”,选择加"星标"或“置顶”
重磅干货,第一时间送达
本文转载自:OpenCV学堂
对于图像来说可以从笛卡尔坐标系统转换到霍夫空间,对于一条直线来说
在笛卡尔坐标系统中表示一条直线有两个参数斜率k与截距b
在霍夫空间中表示一条直线也有两个参数到原点的距离d与角度theta
对于给定任意theta值,都有一个r与之对应,对于点x0=8, y0=6,在霍夫空间有如下的曲线:
当有很多点在霍夫空间的曲线相交于一点时候
就说明这些点具有相同的theta与r,即它们都属于同一条直线,而参数theta与r就是该直线在霍夫空间的直线参数方程。
OpenCV中标准霍夫直线检测源码部分:
// stage 1. fill accumulator
for( i = 0; i < height; i++ )
for( j = 0; j < width; j++ )
{
if( image[i * step + j] != 0 )
for(int n = 0; n < numangle; n++ )
{
int r = cvRound( j * tabCos[n] + i * tabSin[n] );
r += (numrho - 1) / 2;
accum[(n+1) * (numrho+2) + r+1]++;
}
}
// stage 2. find local maximums
findLocalMaximums( numrho, numangle, threshold, accum, _sort_buf );
// stage 3. sort the detected lines by accumulator value
std::sort(_sort_buf.begin(), _sort_buf.end(), hough_cmp_gt(accum));
// stage 4. store the first min(total,linesMax) lines to the output buffer
linesMax = std::min(linesMax, (int)_sort_buf.size());
double scale = 1./(numrho+2);
lines.create(linesMax, 1, type);
Mat _lines = lines.getMat();
for( i = 0; i < linesMax; i++ )
{
LinePolar line;
int idx = _sort_buf[i];
int n = cvFloor(idx*scale) - 1;
int r = idx - (n+1)*(numrho+2) - 1;
line.rho = (r - (numrho - 1)*0.5f) * rho;
line.angle = static_cast(min_theta) + n * theta;
if (type == CV_32FC2)
{
_lines.at(i) = Vec2f(line.rho, line.angle);
}
else
{
CV_DbgAssert(type == CV_32FC3);
_lines.at(i) = Vec3f(line.rho, line.angle, (float)accum[idx]);
}
}
OpenCV中关于霍夫直线检测有两个API,一个被称为标准霍夫变换直线检测,另外一个叫霍夫直线检测。二者之间的区别在于,前者会直接输出theta与r,还有累加和,后者会直接输出相关线段的平面坐标。
void cv::HoughLines(
InputArray image, // 输入参数
OutputArray lines, // 输出结果vector, vector
double rho, // 距离步长d=1, 是指该直线到原点距离,对于屏幕坐标是左上角点
double theta, // 角度步长1°
int threshold, // 阈值,是指累加数目
double srn = 0, // 多尺度检测需要,默认为0
double stn = 0, // 多尺度检测需要,默认为0
double min_theta = 0, // 直线旋转角度
double max_theta = CV_PI // 直线旋转角度
)
对输出的结果是Vec2f的话为(r, theta)如果是Vec3f的话为(r, theta, votes)
当r值大于零的时候表示直线在X轴下方有垂直距离
当r值小于零的时候表示直线在X轴上方有垂直距离
首先对输入图像进行二值化
// 二值化
Mat dst, gray, binary;
cvtColor(src, gray, COLOR_BGR2GRAY);
threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
imshow("binary", binary);
标准霍夫直线检测代码如下
// 绘制直线
Point pt1, pt2;
for (size_t i = 0; i < lines.size(); i++)
{
float rho = lines[i][0]; // 距离
float theta = lines[i][1]; // 角度
float votes = lines[i][2]; // 累加
printf("rho %.2f, theta : %.2f, votes : %.2f \n", rho, theta, votes);
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(src, pt1, pt2, Scalar(0, 0, 255), 1, LINE_AA);
}
imshow("contours", src);
对上述的代码解释,其中x0与y0是直线上的点,1000是表示对改点延长到直线上距离,上述代码计算公式很多人不理解,特手绘白纸一张如下:
假设有如下图像:
通过标准霍夫直线变换实现如下直线分类与最大直线提取
左侧倾斜直线
右侧倾斜直线
水平或者垂直线
长度最大直线
代码实现如下
void hough_lines_demo(Mat &image, Mat &binary) {
// 标准霍夫直线检测
vector lines;
HoughLines(binary, lines, 1, CV_PI / 180, 100, 0, 0);
// 绘制直线
Point pt1, pt2;
for (size_t i = 0; i < lines.size(); i++)
{
float rho = lines[i][0]; // 距离
float theta = lines[i][1]; // 角度
float votes = lines[i][2]; // 累加
printf("rho %.2f, theta : %.2f, votes : %.2f \n", rho, theta, votes);
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));
int angle = round((theta / CV_PI) * 180);
printf("angle : %d\n", angle);
if (i == 0) {
line(image, pt1, pt2, Scalar(0, 255, 0), 1, LINE_AA);
putText(image, "max-line", Point((pt1.x + pt2.x) / 2, (pt1.y + pt2.y) / 2), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 255), 2, 8);
continue;
}
if (rho > 0) { // 右倾
line(image, pt1, pt2, Scalar(0, 0, 255), 1, LINE_AA);
if (angle == 90) { // 水平线
line(image, pt1, pt2, Scalar(255, 255, 0), 1, LINE_AA);
}
if (angle <= 1) { // 垂直线
line(image, pt1, pt2, Scalar(0, 255, 255), 1, LINE_AA);
}
}
else { // 左倾
line(image, pt1, pt2, Scalar(255, 0, 0), 1, LINE_AA);
}
}
imshow("result", image);
}
运行结果如下:
下载1:OpenCV-Contrib扩展模块中文版教程
在「小白学视觉」公众号后台回复:扩展模块中文教程,即可下载全网第一份OpenCV扩展模块教程中文版,涵盖扩展模块安装、SFM算法、立体视觉、目标跟踪、生物视觉、超分辨率处理等二十多章内容。
下载2:Python视觉实战项目52讲
在「小白学视觉」公众号后台回复:Python视觉实战项目,即可下载包括图像分割、口罩检测、车道线检测、车辆计数、添加眼线、车牌识别、字符识别、情绪检测、文本内容提取、面部识别等31个视觉实战项目,助力快速学校计算机视觉。
下载3:OpenCV实战项目20讲
在「小白学视觉」公众号后台回复:OpenCV实战项目20讲,即可下载含有20个基于OpenCV实现20个实战项目,实现OpenCV学习进阶。
交流群
欢迎加入公众号读者群一起和同行交流,目前有SLAM、三维视觉、传感器、自动驾驶、计算摄影、检测、分割、识别、医学影像、GAN、算法竞赛等微信群(以后会逐渐细分),请扫描下面微信号加群,备注:”昵称+学校/公司+研究方向“,例如:”张三 + 上海交大 + 视觉SLAM“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~