1. Canny边缘检测
Canny 是最经典、最常用的边缘检测算法 ,目标:低误检、定位准、边缘单像素细线条
2. 算法四大核心步骤
- 高斯滤波降噪
边缘检测对噪声极敏感,先用高斯模糊抹平噪点(Canny 内部自带)。 - Sobel 求梯度幅值 + 方向
计算每个像素灰度变化大小、梯度方向(0°、45°、90°、135° 四类)。 - 非极大值抑制(NMS)【Canny 精髓】
把梯度方向上不是局部最大值的像素置 0,把宽粗的边缘压缩成单像素细线,这是 Sobel 没有的一步OpenCV-Pyt...。 - 双阈值滞后判断(Hysteresis)
- 梯度 > 高阈值:强边缘(确定是边缘)
- 梯度 < 低阈值:直接舍弃,不是边缘
- 中间(弱边缘):只有和强边缘连通,才保留,否则丢弃
推荐比例:高阈值:低阈值 = 2:1 ~ 3:1,你代码里 40 : 120 = 1:3,是标准配置OpenCV
3. 函数原型
cpp
void Canny(
InputArray image,
OutputArray edges,
double threshold1, // 低阈值
double threshold2, // 高阈值
int apertureSize = 3, // Sobel核大小
bool L2gradient = false // 梯度计算方式
);
cpp
void MainWindow::onCanny()
{
cv::Mat gray;
cv::cvtColor(srcMat, gray,cv::COLOR_BGR2GRAY);
cv::Mat dst;
cv::Canny(gray,srcMat, 40, 120);
showMat(srcMat);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Sobel X
cpp
void MainWindow::on_btnSobelX_clicked()
{
cv::Mat gray, sobel;
cv::cvtColor(srcMat, gray, cv::COLOR_BGR2GRAY);
cv::Sobel(gray, sobel, CV_16S, 1, 0, 3);
cv::convertScaleAbs(sobel, srcMat);
showMat(srcMat);
}
Laplacian
cpp
void MainWindow::on_btnLaplacian_clicked()
{
cv::Mat gray, lap;
cv::cvtColor(srcMat, gray, cv::COLOR_BGR2GRAY);
cv::Laplacian(gray, lap, CV_16S, 3);
cv::convertScaleAbs(lap, srcMat);
showMat(srcMat);
}
4. 轮廓检测
cpp
void MainWindow::on_btnContour_clicked()
{
// 1. 定义两个Mat,用来存灰度图、二值图
cv::Mat gray, bin;
// 2. BGR彩色图 → 单通道灰度图
cv::cvtColor(srcMat, gray, cv::COLOR_BGR2GRAY);
// 3. 灰度图 → 二值图(黑白图)
cv::threshold(gray, bin, 100, 255, cv::THRESH_BINARY);
// 4. 轮廓容器:contours存所有轮廓点集;hierarchy存轮廓层级关系
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
// 5. 查找轮廓
cv::findContours(bin, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
// 6. 拷贝原图,在副本上画轮廓,避免直接修改原图
cv::Mat draw = srcMat.clone();
// 7. 在draw图上绘制轮廓
cv::drawContours(draw, contours, -1, cv::Scalar(0,0,255), 2);
// 8. 把画完轮廓的图赋值给srcMat,替换当前图
srcMat = draw;
// 9. 调用自定义函数刷新UI显示
showMat(srcMat);
}
1. threshold(gray, bin, 100, 255, cv::THRESH_BINARY)
阈值二值化:
gray:输入灰度图bin:输出二值图100:阈值255:大于阈值的像素设置成的值THRESH_BINARY规则:- 像素 > 100 → 置 255(白色)
- 像素 ≤ 100 → 置 0(黑色)
⚠️ 坑点:这个阈值 100 是固定死的,光照一变效果就崩,后面可以改成自适应阈值
adaptiveThreshold
2. std::vector<std::vector<cv::Point>> contours
contours:轮廓数组- 外面一层
vector:有多少个轮廓 - 里面一层
vector<cv::Point>:一个轮廓上所有的坐标点
- 外面一层
示例:
contours[0]就是第 0 个轮廓的全部点
3. std::vector<cv::Vec4i> hierarchy
轮廓层级信息,每个轮廓对应一个 Vec4i = [next, prev, child, parent]
- 本代码用的是
RETR_EXTERNAL,只找最外层轮廓,不抓取内部孔洞,所以 hierarchy 基本用不上
4. findContours(bin, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE)
这是核心函数:从二值图里寻找白色区域的轮廓
- 参数 1:输入图,必须是二值图(黑白)
- 参数 2、3:输出轮廓和层级
cv::RETR_EXTERNAL:只提取最外层轮廓 ,忽略内部孔洞轮廓(比如圆环,只会拿到外圈,内圈丢掉) 可选对比:
RETR_TREE:提取所有轮廓,保留完整父子嵌套关系(适合孔洞检测)cv::CHAIN_APPROX_SIMPLE:轮廓点压缩 比如一条直线,只保留起点终点,丢掉中间冗余点,节省内存; 另一个选项CHAIN_APPROX_NONE:保存轮廓上每一个像素点,点会非常多
5. drawContours(draw, contours, -1, cv::Scalar(0,0,255), 2)
在图像上绘制轮廓
draw:在哪张图上画contours:轮廓集合-1:绘制所有轮廓;填 0 就是只画第 0 个轮廓cv::Scalar(0,0,255):BGR,也就是红色2:线条粗细,单位像素;填cv::FILLED就是把轮廓内部填充
6. srcMat = draw; showMat(srcMat);
把画好轮廓的图覆盖掉成员变量 srcMat,下一次再点按钮,会在上一次画了红线的图上继续处理。