霍夫变换(Hough Transform)是一种经典的图像处理与计算机视觉算法,广泛用于检测图像中的几何形状,例如直线、圆、椭圆等。其核心思想是将图像空间中的“点”映射到参数空间中的“曲线”,从而将形状检测问题转化为参数空间中的峰值检测问题。
输入:边缘图像(如经过 Canny 边缘检测)
输出:一组满足几何模型的形状(如直线、圆)
关键思想:
y=kx+by = kx + by=kx+b
但当直线垂直时(k→∞k \to \inftyk→∞),斜截式不适用,因此使用极坐标式更稳定。
任意一条直线可以表示为:
ρ=xcosθ+ysinθ \rho = x \cos \theta + y \sin \theta ρ=xcosθ+ysinθ
对于一个边缘点 (x0,y0)(x_0, y_0)(x0,y0),代入:
ρ=x0cosθ+y0sinθ \rho = x_0 \cos \theta + y_0 \sin \theta ρ=x0cosθ+y0sinθ
构建一个二维数组 [ρ,θ][ \rho, \theta ][ρ,θ] 作为投票累加器
对图像中每个边缘点:
最终累加器中峰值即代表图像中可能存在的直线
霍夫变换算法步骤(直线)
公式推导与几何意义
对于每个点 (x,y)(x, y)(x,y) 与一个方向 θ\thetaθ,其对应的直线 ρ=xcosθ+ysinθ\rho = x \cos \theta + y \sin \thetaρ=xcosθ+ysinθ 是点法式直线:
扩展到圆的霍夫变换(Hough Circle Transform)
圆的一般方程为:
(x−a)2+(y−b)2=r2 (x - a)^2 + (y - b)^2 = r^2 (x−a)2+(y−b)2=r2
参数空间为 (a,b,r)(a, b, r)(a,b,r),每个边缘点投票所有可能的圆心与半径:
#include
#include
#include
#include
constexpr double DEG2RAD = M_PI / 180.0;
struct Line {
double rho;
double theta;
int votes;
};
std::vector<Line> HoughLineTransform(const std::vector<std::pair<int, int>>& edge_points,
int width, int height,
int theta_steps = 180, int threshold = 100) {
const int diag_len = static_cast<int>(std::sqrt(width * width + height * height));
const int rho_max = diag_len;
const int rho_min = -diag_len;
const int rho_range = 2 * diag_len;
std::vector<std::vector<int>> accumulator(rho_range, std::vector<int>(theta_steps, 0));
// 遍历每个边缘点
for (const auto& p : edge_points) {
int x = p.first;
int y = p.second;
for (int t = 0; t < theta_steps; ++t) {
double theta = t * DEG2RAD;
double rho = x * std::cos(theta) + y * std::sin(theta);
int rho_idx = static_cast<int>(std::round(rho)) + diag_len;
if (rho_idx >= 0 && rho_idx < rho_range) {
accumulator[rho_idx][t]++;
}
}
}
// 提取票数超过阈值的结果
std::vector<Line> detected_lines;
for (int r = 0; r < rho_range; ++r) {
for (int t = 0; t < theta_steps; ++t) {
int votes = accumulator[r][t];
if (votes > threshold) {
detected_lines.push_back({
.rho = r - diag_len,
.theta = t * DEG2RAD,
.votes = votes
});
}
}
}
return detected_lines;
}
int main() {
int width = 200, height = 200;
// 模拟一条对角线 y = x 的边缘点
std::vector<std::pair<int, int>> edge_points;
for (int i = 0; i < 200; ++i) {
edge_points.emplace_back(i, i);
}
auto lines = HoughLineTransform(edge_points, width, height, 180, 50);
std::cout << "Detected Lines:" << std::endl;
for (const auto& line : lines) {
std::cout << "rho: " << line.rho << ", theta (deg): " << line.theta * 180 / M_PI
<< ", votes: " << line.votes << std::endl;
}
return 0;
}
// 点 (x0, y0) 在线上:
x0 = rho * cos(theta)
y0 = rho * sin(theta)
// 方向向量:
dx = -sin(theta), dy = cos(theta)
// 可绘制直线 (x0 ± dx * scale, y0 ± dy * scale)
#include
#include
int main() {
cv::Mat img = cv::imread("line_image.png", cv::IMREAD_GRAYSCALE);
if (img.empty()) return -1;
// 1. 边缘检测
cv::Mat edges;
cv::Canny(img, edges, 50, 150, 3);
// 2. 霍夫变换检测直线
std::vector<cv::Vec2f> lines;
cv::HoughLines(edges, lines, 1, CV_PI / 180, 100); // ρ=1像素, θ=1°, 阈值=100票
// 3. 显示结果
cv::Mat color_img;
cv::cvtColor(img, color_img, cv::COLOR_GRAY2BGR);
for (size_t i = 0; i < lines.size(); i++) {
float rho = lines[i][0], theta = lines[i][1];
double a = cos(theta), b = sin(theta);
double x0 = a * rho, y0 = b * rho;
cv::Point pt1(cvRound(x0 + 1000 * (-b)), cvRound(y0 + 1000 * (a)));
cv::Point pt2(cvRound(x0 - 1000 * (-b)), cvRound(y0 - 1000 * (a)));
cv::line(color_img, pt1, pt2, cv::Scalar(0, 0, 255), 2);
}
cv::imshow("Detected Lines", color_img);
cv::waitKey(0);
return 0;
}