c/c++的opencv霍夫变换

OpenCV中的霍夫变换 (C/C++) Hough Transform

霍夫变换 (Hough Transform) 是一种在图像分析中用于检测几何形状(如直线、圆形等)的特征提取技术。它通过一种投票机制在参数空间中寻找特定形状的实例。OpenCV 库为 C++ 开发者提供了强大且易用的霍夫变换函数。


1. 霍夫直线变换 (Hough Line Transform) 📏

霍夫直线变换用于检测图像中的直线。其基本思想是将图像空间中的点映射到参数空间(通常是极坐标 ( ρ , θ ) (\rho, \theta) (ρ,θ) 空间),并在参数空间中寻找峰值,这些峰值对应于图像空间中的直线。

直线方程: ρ = x cos ⁡ θ + y sin ⁡ θ \rho = x \cos\theta + y \sin\theta ρ=xcosθ+ysinθ

其中:

  • ρ \rho ρ (rho) 是从原点到直线的垂直距离。
  • θ \theta θ (theta) 是这条垂直线与 x 轴之间的角度。

1.1 标准霍夫变换 (Standard Hough Transform - SHT)

cv::HoughLines 函数实现了标准霍夫变换。它返回一个包含 ( ρ , θ ) (\rho, \theta) (ρ,θ) 对的向量。

函数原型:

cpp 复制代码
void cv::HoughLines(
    cv::InputArray image,         // 输入图像 (必须是8位单通道二值图像,通常是Canny边缘检测的输出)
    cv::OutputArray lines,        // 输出向量,存储检测到的直线的参数 (cv::Vec2f 表示 rho 和 theta)
    double rho,                   // rho 的累加器分辨率 (以像素为单位)
    double theta,                 // theta 的累加器分辨率 (以弧度为单位)
    int threshold,                // 累加器阈值参数,只有累加值大于该阈值的直线才会被检测到
    double srn = 0,               // 对于多尺度霍夫变换,它是 rho 的除数。如果为0,则使用经典霍夫变换。
    double stn = 0,               // 对于多尺度霍夫变换,它是 theta 的除数。如果为0,则使用经典霍夫变换。
    double min_theta = 0,         // 检测直线的最小角度 (弧度)
    double max_theta = CV_PI      // 检测直线的最大角度 (弧度)
);

主要步骤:

  1. 边缘检测:通常使用 cv::Canny
  2. 应用 cv::HoughLines
  3. 遍历检测到的直线参数,并在原图上绘制。

示例代码:

cpp 复制代码
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>

int main() {
    cv::Mat src = cv::imread("your_image_with_lines.png", cv::IMREAD_GRAYSCALE);
    if (src.empty()) {
        std::cerr << "Error: Could not load image." << std::endl;
        return -1;
    }

    cv::Mat edges;
    cv::Canny(src, edges, 50, 200, 3); // 1. 边缘检测

    std::vector<cv::Vec2f> lines; // (rho, theta)
    // 2. 标准霍夫变换
    cv::HoughLines(edges, lines, 1, CV_PI / 180, 150, 0, 0);

    cv::Mat color_dst;
    cv::cvtColor(edges, color_dst, cv::COLOR_GRAY2BGR); // 用于绘制彩色线条

    // 3. 绘制直线
    for (size_t i = 0; i < lines.size(); i++) {
        float rho = lines[i][0], theta = lines[i][1];
        cv::Point pt1, pt2;
        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));
        cv::line(color_dst, pt1, pt2, cv::Scalar(0, 0, 255), 1, cv::LINE_AA);
    }

    cv::imshow("Source", src);
    cv::imshow("Detected Lines (Standard Hough)", color_dst);
    cv::waitKey(0);
    return 0;
}

1.2 概率霍夫变换 (Progressive Probabilistic Hough Transform - PPHT)

cv::HoughLinesP 函数实现了概率霍夫变换。它是一种更高效的霍夫变换,因为它只对图像中的一个随机子集点进行投票。它还直接返回检测到的线段的两个端点。

函数原型:

cpp 复制代码
void cv::HoughLinesP(
    cv::InputArray image,         // 输入图像 (8位单通道二值图像)
    cv::OutputArray lines,        // 输出向量,存储检测到的线段的端点 (cv::Vec4i 表示 x1, y1, x2, y2)
    double rho,                   // rho 的累加器分辨率 (以像素为单位)
    double theta,                 // theta 的累加器分辨率 (以弧度为单位)
    int threshold,                // 累加器阈值参数
    double minLineLength = 0,     // 检测线段的最小长度
    double maxLineGap = 0         // 同一直线上两点之间的最大允许间隙,以将它们连接起来
);

主要步骤:

  1. 边缘检测:通常使用 cv::Canny
  2. 应用 cv::HoughLinesP
  3. 遍历检测到的线段端点,并在原图上绘制。

示例代码:

cpp 复制代码
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>

int main() {
    cv::Mat src = cv::imread("your_image_with_lines.png", cv::IMREAD_COLOR);
    if (src.empty()) {
        std::cerr << "Error: Could not load image." << std::endl;
        return -1;
    }

    cv::Mat gray, edges;
    cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY);
    cv::Canny(gray, edges, 50, 200, 3); // 1. 边缘检测

    std::vector<cv::Vec4i> linesP; // (x1, y1, x2, y2)
    // 2. 概率霍夫变换
    cv::HoughLinesP(edges, linesP, 1, CV_PI / 180, 80, 50, 10);

    // 3. 绘制线段
    for (size_t i = 0; i < linesP.size(); i++) {
        cv::Vec4i l = linesP[i];
        cv::line(src, cv::Point(l[0], l[1]), cv::Point(l[2], l[3]), cv::Scalar(0, 0, 255), 2, cv::LINE_AA);
    }

    cv::imshow("Source", gray); // 显示灰度图或原彩色图
    cv::imshow("Detected Lines (Probabilistic Hough)", src);
    cv::waitKey(0);
    return 0;
}

2. 霍夫圆变换 (Hough Circle Transform) ⭕

霍夫圆变换用于检测图像中的圆形。圆的方程是 ( x − a ) 2 + ( y − b ) 2 = r 2 (x-a)^2 + (y-b)^2 = r^2 (x−a)2+(y−b)2=r2,其中 ( a , b ) (a,b) (a,b) 是圆心, r r r 是半径。因此,参数空间是三维的。OpenCV 使用一种称为霍夫梯度法 (Hough Gradient Method) 的两阶段算法,它首先检测圆心,然后找到最佳半径。

函数原型:

cpp 复制代码
void cv::HoughCircles(
    cv::InputArray image,         // 输入图像 (8位单通道灰度图像)
    cv::OutputArray circles,      // 输出向量,存储检测到的圆 (cv::Vec3f 表示 x, y, radius 或 cv::Vec4f x, y, radius, votes)
    int method,                   // 检测方法,目前仅支持 cv::HOUGH_GRADIENT 和 cv::HOUGH_GRADIENT_ALT
    double dp,                    // 累加器图像分辨率与输入图像分辨率的反比。例如,如果 dp=1,则累加器具有与输入图像相同的分辨率。如果 dp=2,累加器的宽度和高度只有一半。对于 HOUGH_GRADIENT_ALT,推荐值为 dp=1.5。
    double minDist,               // 检测到的圆心之间的最小距离。如果参数太小,除了真实的圆之外,可能还会错误地检测到多个邻近的圆。如果太大,可能会漏掉一些圆。
    double param1 = 100,          // Canny边缘检测器的高阈值 (低阈值是其一半)。对于 HOUGH_GRADIENT_ALT,这是推荐的霍夫变换累加器阈值。
    double param2 = 100,          // 圆心检测的累加器阈值。它越小,就越容易检测到假圆。对于 HOUGH_GRADIENT_ALT,这是圆的 "完美度" 度量。越接近1,检测到的圆形状越好。
    int minRadius = 0,            // 圆的最小半径
    int maxRadius = 0             // 圆的最大半径 (如果 <= 0, 则使用图像的最大维度;如果 < 0, 则返回圆心而不寻找半径)
);

注意: cv::HOUGH_GRADIENT_ALT 通常能提供更好的结果,但可能较慢。

主要步骤:

  1. 图像预处理:通常转换为灰度图,并进行平滑处理(如高斯模糊 cv::GaussianBlur 或中值滤波 cv::medianBlur)以减少噪声。
  2. 应用 cv::HoughCircles
  3. 遍历检测到的圆,并在原图上绘制。

示例代码:

cpp 复制代码
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>

int main() {
    cv::Mat src = cv::imread("your_image_with_circles.png", cv::IMREAD_COLOR);
    if (src.empty()) {
        std::cerr << "Error: Could not load image." << std::endl;
        return -1;
    }

    cv::Mat gray;
    cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY); // 1. 转换为灰度图

    // 1. 平滑处理以减少噪声,避免错误检测
    cv::medianBlur(gray, gray, 5); // 中值滤波对于椒盐噪声效果好

    std::vector<cv::Vec3f> circles; // (x, y, radius)
    // 2. 霍夫圆变换
    // 参数需要根据具体图像进行仔细调整
    cv::HoughCircles(gray, circles, cv::HOUGH_GRADIENT,
                     1,          // dp: 累加器分辨率与图像分辨率的反比
                     gray.rows / 8, // minDist: 圆心之间的最小距离 (可调整)
                     100,        // param1: Canny边缘检测的高阈值
                     30,         // param2: 圆心累加器阈值 (值越小,检测到的圆越多,也可能包括假圆)
                     10,         // minRadius: 最小半径
                     100         // maxRadius: 最大半径 (根据你的图像调整)
    );

    // 3. 绘制检测到的圆
    for (size_t i = 0; i < circles.size(); i++) {
        cv::Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
        int radius = cvRound(circles[i][2]);
        // 绘制圆心
        cv::circle(src, center, 3, cv::Scalar(0, 255, 0), -1, 8, 0);
        // 绘制圆轮廓
        cv::circle(src, center, radius, cv::Scalar(0, 0, 255), 2, 8, 0);
    }

    cv::imshow("Source", src); // 在原彩色图上绘制
    cv::imshow("Detected Circles", src);
    cv::waitKey(0);
    return 0;
}

3. 重要提示与技巧 💡

  • 参数调整 :霍夫变换的参数(如 threshold, rho, theta, minDist, param1, param2, minRadius, maxRadius 等)对检测结果影响很大。通常需要根据具体的应用场景和图像特性进行仔细调整和实验。
  • 预处理
    • 对于直线检测cv::Canny 边缘检测是必不可少的步骤,其阈值也会影响最终结果。
    • 对于圆检测 ,适当的平滑处理(如 cv::medianBlurcv::GaussianBlur)有助于减少噪声,从而提高检测的准确性。
  • 图像质量:输入图像的质量直接影响霍夫变换的性能。清晰、对比度好的图像更容易获得好的检测结果。
  • 选择合适的变换类型
    • 如果需要检测到精确的线段端点并且关心效率,cv::HoughLinesP (概率霍夫变换) 通常是更好的选择。
    • 如果需要图像中所有可能的直线信息(参数形式),则使用 cv::HoughLines (标准霍夫变换)。
  • cv::HOUGH_GRADIENT_ALT :对于圆检测,可以尝试使用 method = cv::HOUGH_GRADIENT_ALT,它可能提供更鲁棒的检测结果,但其参数(特别是 param1param2)的含义与 cv::HOUGH_GRADIENT 不同,需要查阅文档并进行调整。

4. 总结 🚀

OpenCV 中的霍夫变换为直线和圆形的检测提供了强大而灵活的工具。理解其基本原理和各个参数的含义,结合适当的图像预处理,可以有效地在各种计算机视觉应用中识别这些基本几何形状。实践和参数调整是掌握霍夫变换的关键。

相关推荐
夜泉_ly7 分钟前
Qt -使用OpenCV得到SDF
c++·qt·算法
?!7141 小时前
算法打卡第11天
数据结构·c++·算法·哈希算法
?!7144 小时前
Socket编程之TCP套件字
linux·网络·c++·网络协议·tcp/ip
小指纹5 小时前
2025山东CCPC题解
c++·算法
小老鼠不吃猫6 小时前
C接口 中文字符问题
c语言·开发语言
技术程序猿华锋6 小时前
Void:免费且隐私友好的 AI 编码利器,挑战 Cursor 地位?
c++·人工智能·mfc
倔强的石头1067 小时前
【C++指南】C++ list容器完全解读(二):list模拟实现,底层架构揭秘
c++·架构·list
木子.李3479 小时前
数据结构-算法学习C++(入门)
数据库·c++·学习·算法
半青年10 小时前
IEC61850规约客户端软件开发实战(第二章)
java·c++·qt·网络协议·c#·信息与通信·iec61850
moz与京11 小时前
【数据结构】字符串操作整理(C++)
开发语言·数据结构·c++