openCV学习指南
本文最后更新于:2023年9月23日 下午
一些opencv的函数积累,还有传统opencv处理项目的流程整理
坐标转换
pnp解算
1 |
|
函数solvepnp接收一组对应的3D坐标和2D坐标,计算得到两组坐标对应的几何变换(旋转矩阵rvec,平移矩阵tvec),从而建立相机拍摄2D图像中物体坐标和3D世界坐标系中物体坐标的映射关系。
重映射
1 |
|
根据所给的3D坐标和已知的几何变换来求解投影后的2D坐标
相机标定
同时标定两个摄像头
1 |
|
能够求出两个摄像头的内外参数矩阵,还能够得出两个摄像头的位置关系R,T
立体矫正
1 |
|
计算每个摄像机(实际上)的旋转矩阵,从而使两个摄像机图像平面成为同一平面。因此,这使得所有的外极线平行,从而简化了稠密立体对应问题。
图像处理
预处理流程
常规步骤:
图像颜色空间转换:RGB2HSV
- 原因:HSV中的H表示色调,S表示饱和度,V表示亮度。不同颜色在HSV空间有严格的分量范围,从而将颜色进行量化。
- 代码:
1
2Mat imgHSV;
cvtColor(imgOriginal, imgHSV, COLOR_BGR2HSV);
直方图均衡化(三个通道各自均衡化再组合)
- 目的:通过拉伸像素强度分布范围来增强图像对比度,利于后面的二值化处理。
- 效果对比:
- 代码:
1
2
3
4vector<Mat> hsvSplit;
split(imgHSV, hsvSplit); //通道分离
equalizeHist(hsvSplit[2], hsvSplit[2]); //通道各自均衡化
merge(hsvSplit, imgHSV); //通道合并
阈值处理
- 目的:图像进行目标分割,可用于目标检测、图像增强等。
- 常用的阈值处理办法:
二值化阈值处理
:threshold函数,大于阈值设为最大值,小于就是0。(最简单但是感觉不太好用,轮廓提取不明显。)
自适应阈值处理
:根据图像的局部特征,自动确定每个像素点的阈值,能够在不同光照条件下得到更好的效果。(但是不能筛选特定颜色的轮廓目标,可以区分背景和前景)
双阈值化操作
:将在两个阈值内的像素值设置为白色(255),而不在阈值区间内的像素值设置为黑色(0),可以用来筛选指定颜色的物体。 - 代码:
1
2Mat imgThresholded;
inRange(imgHSV, LOWERB, UPPERB, imgThresholded); //双阈值化操作
开闭操作:
- 目的:
开运算
:先腐蚀后膨胀,用于消除细小物体、在窄区域分离物体、平滑大物体边界。
闭运算
:先膨胀后腐蚀,用于填充物体空洞、消除噪声、连接邻近物体、平滑边界。 - 代码:
1
2
3
4
5
6
7//得到一个矩形卷积核
Mat element = getStructuringElement(MORPH_RECT, Size(8, 8));
//闭操作,填充物体空洞
morphologyEx(imgThresholded, imgThresholded, MORPH_CLOSE, element);
dilate(imgThresholded, imgThresholded, 300 * 300, Point(-1, -1), 1);
//开操作,去除噪点
morphologyEx(imgThresholded, imgThresholded, MORPH_OPEN, element);
- 目的:
轮廓修复
预处理的逻辑比较通用,但是在实际光线和物体呈现角度的因素影响下,预处理得到的轮廓是不太好的,比如圆可能识别出来是个月牙、形状被分割成好几个物体、正方形可能只识别了一个多边形的轮廓,并没有完整的被抠出来等等,这样的预处理效果不足以用于后续的轮廓判断,因此需要进行轮廓修复。
- 修复效果图:
凸包检测+多边形填充+闭运算
1 |
|
轮廓判断
整体逻辑很简单,使用多边形拟合得到轮廓的边数信息,再与目标形状的边数进行比对判断是否为所求形状。
1 |
|
- 但是现实情况往往更复杂,以下为笔者在轮廓识别的过程中进行的优化:
圆度检测
:
其他图像可能在轮廓修复和开闭操作之后,棱角不明显,接近圆形,从而导致误识别,因此引入圆度检测。1
2
3
4
5
6
7
8
9
10
11bool ifCircle(vector<Point>contour) {
int area = contourArea(contour); //计算轮廓面积
float len = arcLength(contour, true); //计算轮廓周长
float roundness = (4 * CV_PI * area) / (len * len); //计算圆度
// cout << "未确定圆的圆度:" << roundness << endl;
if (roundness < 0.92) {
//cout << "没过检测,圆度:" << roundness << endl;
return false;
}
return true;
}矩形检测
:圆形在预处理之后,有概率被误识别为矩形;为更好的区分矩形和圆形,引入矩形检测。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21bool ifRect(vector<Point>contour) {
float rectangularity;
//计算最小外接矩形的面积:
RotatedRect minrect = minAreaRect(contour); //最小外接矩形
int area = contourArea(contour); //计算轮廓面积
int Sminrect = minrect.size.height * minrect.size.width;
if (Sminrect == 0) rectangularity = 0;
else rectangularity = (float)area / Sminrect;
// cout << "矩形度:"<<rectangularity<<"最小外接矩形面积:"<< Sminrect<<"轮廓面积:" << area << endl;
if (rectangularity <0.86)return false;
return true;
}
*************在轮廓函数中,采用矩形度和圆度双重检测,有效提高矩形的识别精确度****************
if (objCor == 4) {
//矩形度检测
if (ifCircle(contour[i])|| ! ifRect(contour[i])) {
cout << "矩形没过检测!!" << endl;
continue;
};
在图片上进行标记
绘制轮廓
1 |
|
框出目标物体
1 |
|
标注点
1 |
|
计算点的坐标
找圆点
- 霍夫检测(我感觉很难调参而且精度不太行)
- 最小二乘法拟合圆(没试过,但是据说可以,就是得自己造轮子)
- 先识别得到一个圆度大于0.9的圆,再找最小外接矩形,矩形中点即为圆心(有点偷奸耍滑,但是几何上说得通嘿嘿)
1
2
3
4
5
6
7void CircleCenter(Mat img, Rect rect) {
int x = rect.width / 2;
int y = rect.height / 2;
Point Radius = Point(rect.x + x, rect.y + y);
circle(img, Radius, 3, Scalar(0, 255, 120), -1);
return;
}
找矩形的四个顶点
只要通过矩形检测,四边形就是矩形啦。多边形拟合之后的那个conpoly就是矩形的角点点集。
openCV学习指南
http://zoechen04616.github.io/2023/07/26/openCV学习指南/