文章目录

一、霍夫变换-直线

Hough Line Transform用来做直线检测
前提条件 – 边缘检测已经完成

  1. 平面空间(x,y)到极坐标空间转换;OpenCV + CPP 系列(廿二)霍夫变换(直线、圆)_#include
  2. 对极坐标进行变换,转化为θ与R的关系
      OpenCV + CPP 系列(廿二)霍夫变换(直线、圆)_opencv_02
      OpenCV + CPP 系列(廿二)霍夫变换(直线、圆)_霍夫变换_03
      OpenCV + CPP 系列(廿二)霍夫变换(直线、圆)_霍夫变换_04

​霍夫变换-直线 原理详解​

对于任意一条直线上的所有点来说,变换到极坐标中,从[0~360]空间,可以得到r的大小,属于同一条直线上点在极坐标空 OpenCV + CPP 系列(廿二)霍夫变换(直线、圆)_霍夫变换_05

标准的霍夫变换 cv::HoughLines 从平面坐标转换到霍夫空间,最终输出是 OpenCV + CPP 系列(廿二)霍夫变换(直线、圆)_霍夫变换_05


void HoughLines( InputArray image,     输入图像,必须8-bit的灰度图像


OutputArray

lines,    输出的极坐标来表示直线


double

rho,        极坐标素扫描步长


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


);


霍夫变换直线概率 cv::HoughLinesP最终输出是直线的两个点 OpenCV + CPP 系列(廿二)霍夫变换(直线、圆)_opencv_07


void HoughLinesP( InputArray image,      输入图像,必须8-bit的灰度图像

OutputArray lines,     输出的极坐标来表示直线


double rho,         极坐标像素扫描步长


double theta,        极坐标角度步长,一般取值CV_PI/180


int threshold,        阈值,多少交点的极坐标点才被看成是直线


double minLineLength = 0,  最小直线长度


double maxLineGap = 0 );  最大间隔


头文件 quick_opencv.h:声明类与公共函数

#pragma once
#include <opencv2\opencv.hpp>
using namespace cv;

class QuickDemo {
public:
...
void hough_Demo(Mat& image1);
void laplance_Demo(Mat& image1);
void canny_Demo(Mat& image1);
};

主函数调用该类的公共成员函数

#include <opencv2\opencv.hpp>
#include <quick_opencv.h>
#include <iostream>
using namespace cv;


int main(int argc, char** argv) {
Mat src = imread("D:\\Desktop\\jianbian.png");
if (src.empty()) {
printf("Could not load images...\n");
return -1;
}
QuickDemo qk;
qk.hough_Demo(src1);
qk.hough_Demo(src1);
qk.canny_Demo(src1);
waitKey(0);
destroyAllWindows();
return 0;
}

源文件 quick_demo.cpp:实现类与公共函数

效果演示

void QuickDemo::hough_Demo(Mat& image) {
Mat image_line = image.clone();

// 霍夫直线检测
Mat gau_dst, gray_dst, grad_dst;
GaussianBlur(image_line, gau_dst, Size(3, 3), 10);
cvtColor(gau_dst, gray_dst, COLOR_BGR2GRAY);

vector<Vec4f> plines;
Canny(gray_dst, gray_dst, 100, 200);
imshow("Canny", gray_dst);

HoughLinesP(gray_dst, plines, 5, CV_PI/180, 10, 0, 10);// 斜线检测调整maxlineGap
cout << "线条个数:" << plines.size() << endl;
for (int i = 0; i < plines.size(); i++) {
Vec4f hline = plines[i];
line(image_line, Point(hline[0], hline[1]), Point(hline[2], hline[3]), Scalar(0, 0, 255), 1, 8, 0);
}
imshow("HoughLinesP", image_line);


// 霍夫圆检测
Mat median_dst, gray_median_dst;
vector<Vec3f> pcircles;
medianBlur(image, median_dst, 3);
cvtColor(median_dst, gray_median_dst, COLOR_BGR2GRAY);
HoughCircles(gray_median_dst, pcircles, HOUGH_GRADIENT, 1, 10, 100, 30, 5, 100);
cout << "圆形个数:" << pcircles.size() << endl;

for (int i = 0; i < pcircles.size(); i++) {
Vec3f cc = pcircles[i];
circle(image, Point(cc[0], cc[1]), cc[2], Scalar(255, 0, 0), 2, 8, 0);
circle(image, Point(cc[0], cc[1]), 2, Scalar(255, 0, 0), 2, 8, 0); // 标注圆形
}
imshow("circle", image);
}

OpenCV + CPP 系列(廿二)霍夫变换(直线、圆)_霍夫变换_08

二、霍夫圆检测

算法详情见:​​霍夫圆检测​​​ 官方给出了好的示例。
对直线来说,一条直线能由极径极角 OpenCV + CPP 系列(廿二)霍夫变换(直线、圆)_边缘检测_09 表示,而对于圆来说,我们需要三个参数:圆心OpenCV + CPP 系列(廿二)霍夫变换(直线、圆)_#include_10,半径 OpenCV + CPP 系列(廿二)霍夫变换(直线、圆)_#include_11

笛卡尔坐标系中圆的方程为:

OpenCV + CPP 系列(廿二)霍夫变换(直线、圆)_边缘检测_12


OpenCV + CPP 系列(廿二)霍夫变换(直线、圆)_边缘检测_13


OpenCV + CPP 系列(廿二)霍夫变换(直线、圆)_#include_14

这就意味着每一组(a,b,r)代表一个通过点(x0,y0)的圆。

对于一个给定点(x0,y0),我们可以在三维直角坐标系中,绘出所有通过它的圆。最终我们将得到一条三维的曲线。
由于霍夫线变换圆检测对噪声比较敏感,所以首先要对图像做中值滤波,而且需要更多的检测计算消耗。OpenCV 霍夫圆变换对标准霍夫圆变换做了运算上的优化。它采用的是 “霍夫梯度法”。

基于效率考虑,Opencv中实现的霍夫变换圆检测是基于图像梯度的实现,分为两步:
1. 检测边缘,发现可能的圆心
2. 基于第一步的基础上从候选圆心开始计算最佳半径大小

霍夫梯度法的原理

估计圆心

  1. 把原图做一次 Canny 边缘检测,得到边缘检测的二值图。
  2. 对原始图像执行一次 Sobel 算子,计算出所有像素的邻域梯度值。
  3. 初始化圆心空间 N(a,b),令所有的 N(a,b)=0。
  4. 遍历 Canny 边缘二值图中的所有非零像素点,沿着梯度方向 ( 切线的垂直方向 )画线,将线段经过的所有累加器中的点 (a,b) 的 N(a,b)+=1。

void HoughCircles( InputArray image,      输入图像 ,必须是8位的单通道灰度图像


OutputArray

circles,    输出结果,发现的圆信息


int

method,         HOUGH_GRADIENT


double

dp,         dp = 1 累加器图像的反比分辨率


double

minDist=src_gray.rows/8, 检测到圆心之间的最小距离,否则认为是同心圆-


double

param1 = 100,    canny edge detection low threshold


double

param2 = 100,   中心点累加器阈值 – 候选圆心


int

minRadius = 0,      能检测到的最小圆半径, 默认为0


int

maxRadius = 0      能检测到的最大圆半径, 默认为0


);


OpenCV + CPP 系列(廿二)霍夫变换(直线、圆)_opencv_15

我们也可以使用​​图像矩来检测圆形​