一、开场引言
在工业机器视觉领域,有很多直线检测和计算角度的应用场景。如下图,需要进行晶圆的粗对位校正:
此时需要计算出图像中的近似水平切割道的线条与实际水平线的夹角,从而进行晶圆的位置校正。在这个场景下,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变换,精度定然会有明显提高。不过作为粗对位而言,实际现场基本够用~