Qt+Opencv:基于Hough变换的直线检测

一、开场引言

在工业机器视觉领域,有很多直线检测和计算角度的应用场景。如下图,需要进行晶圆的粗对位校正:

此时需要计算出图像中的近似水平切割道的线条与实际水平线的夹角,从而进行晶圆的位置校正。在这个场景下,Hough变换就派上用场了。

二、认识Hough变换

2.1 Hough变换

Hough变换是一种图像处理技术,用于检测图像中的直线、圆或其他形状。它的基本原理是将图像空间中的特定形状映射到参数空间中,并在参数空间中进行累加,以找到图像中的特定形状。

优点:

  • 对噪声具有一定的鲁棒性,能够在一定程度上抵抗图像中的噪声。
  • 可以检测多种形状,包括直线、圆等。
    缺点:
  • 计算复杂度高,特别是对于大型图像和复杂形状的检测。
  • 对参数空间的离散化和累加需要大量的内存和计算资源。

2.2 概率Hough变换

概率Hough变换是对标准Hough变换的改进,通过随机地选择图像中的像素点来进行累加,从而减少计算量。

优点:

  • 计算量相对较小,适用于大型图像和复杂形状的检测。
  • 对于一些特定的应用场景,如检测长直线,概率Hough变换通常比标准Hough变换更有效。
    缺点:
  • 对于某些形状的检测可能不够精确,特别是在图像中存在大量噪声或形状重叠的情况下。
    比较
  • Hough变换适用于对图像中各种形状的检测,但计算复杂度高。
  • 概率Hough变换通过随机采样来减少计算量,适用于大型图像和复杂形状的检测,但可能牺牲一定的精度。

总的来说,选择使用Hough变换还是概率Hough变换取决于具体的应用场景和对计算效率和检测精度的要求。

三、示例

仍以引言中的图像作为示例,我们在这里作为晶圆级的粗对位,采用概率hough变换即可。代码示例如下:

cpp 复制代码
#ifndef HOUGHLINESDEMO_H
#define HOUGHLINESDEMO_H
class HoughLinesDemo
{
public:
    HoughLinesDemo();

    static int run();
};

#endif // HOUGHLINESDEMO_H
cpp 复制代码
#include "houghlinesdemo.h"
#include <opencv2/opencv.hpp>
#include <iostream>
#include <QDateTime>
#include <vector>
#include <algorithm>

HoughLinesDemo::HoughLinesDemo()
{

}

int HoughLinesDemo::run()
{

    // 读取图像
    cv::Mat image = cv::imread("image/large_field_1.png");
    // cv::Mat image = cv::imread("image/large_field.bmp");

    /// 将图像转为灰度图
    cv::Mat gray;
    cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);

    // 使用Canny边缘检测
    cv::Mat edges;
    cv::Canny(gray, edges, 50, 150, 3);

    // 进行Probabilistic Hough Transform直线检测
    std::vector<cv::Vec4i> lines;
    cv::HoughLinesP(edges, lines, 1, CV_PI/180, 50, 50, 5);


    std::vector<double> angles;
    // 计算每条直线的斜率和与水平线的夹角
    for (size_t i = 0; i < lines.size(); i++) {
        cv::Vec4i l = lines[i];
        cv::line(image, cv::Point(l[0], l[1]), cv::Point(l[2], l[3]), cv::Scalar(0, 0, 255), 2);
        double angle = atan2(l[3] - l[1], l[2] - l[0]) * 180.0 / CV_PI;
        if(angle <= 0){
            angles.push_back(angle);
        }
        std::cout << "Angle with horizontal: " << angle << std::endl;
    }

    // 显示结果
    cv::imshow("Probabilistic Hough Transform", image);
    const QString time = QDateTime::currentDateTime().toString("yy-MM-dd hh-mm-ss");
    QString sImge = QString("result/%1.png").arg(time);
    cv::imwrite(sImge.toStdString(),image);

    // 对角度数据进行排序
    std::sort(angles.begin(), angles.end());

    // 计算中值
    double median;
    if (angles.size() % 2 == 0) {
        median = (angles[angles.size() / 2 - 1] + angles[angles.size() / 2]) / 2.0;
    } else {
        median = angles[angles.size() / 2];
    }
    std::cout << "Median angle: " << median << " degrees" << std::endl;


    // cv 旋转原图
    cv::Point2f center(image.cols / 2.0, image.rows / 2.0); // 旋转中心
    cv::Mat rotationMatrix = cv::getRotationMatrix2D(center, median, 1.0); // 旋转矩阵
    cv::Mat rotatedImage;
    cv::warpAffine(image, rotatedImage, rotationMatrix, image.size());

    // 显示原始图像和旋转后的图像
    cv::imshow("Rotated Image", rotatedImage);

    do { }  while (cvWaitKey(30) < 0);
    cv::destroyAllWindows();

    return 0;
}

四、结果展示

我们在角度数据处理时,采用的中值计算的方式,误差也会比较大。
Median angle: -0.79298 degrees

可以看到原图及计算角度旋转校正之后的图像,图像有明显的校正,但是水平切割道依然不是完美的水平。将种植计算改为计算直方,将概率hough变换换为hough变换,精度定然会有明显提高。不过作为粗对位而言,实际现场基本够用~

相关推荐
ULTRA??3 分钟前
C加加中的结构化绑定(解包,折叠展开)
开发语言·c++
远望清一色19 分钟前
基于MATLAB的实现垃圾分类Matlab源码
开发语言·matlab
confiself28 分钟前
大模型系列——LLAMA-O1 复刻代码解读
java·开发语言
XiaoLeisj40 分钟前
【JavaEE初阶 — 多线程】Thread类的方法&线程生命周期
java·开发语言·java-ee
杜杜的man43 分钟前
【go从零单排】go中的结构体struct和method
开发语言·后端·golang
幼儿园老大*44 分钟前
走进 Go 语言基础语法
开发语言·后端·学习·golang·go
半桶水专家1 小时前
go语言中package详解
开发语言·golang·xcode
llllinuuu1 小时前
Go语言结构体、方法与接口
开发语言·后端·golang
cookies_s_s1 小时前
Golang--协程和管道
开发语言·后端·golang
王大锤43911 小时前
golang通用后台管理系统07(后台与若依前端对接)
开发语言·前端·golang