写在前面的话:
本博客以上交大2019赛季步兵视觉代码为基础学习,仅作个人笔记使用,若有侵权请在评论区留言或私信我。
图像预处理
1-1 RGB通道加权处理
描述: 使用通道拆分函数对载入的视频或图像进行通道加权处理通道拆分:一张图片由RGB三原色混合而成,通道拆分就是把原图拆分成R、G、B三个通道,方便我们后续对图片进行识别和处理,在RM中一般只用识别(蓝色/红色)。
上交大源码:
1. cv::Mat color_channel; //颜色通道
2. std::vector<cv::Mat> channels; // 定义一个空的vector容器储存图像
3.
4. cv::split(src, channels); /************************/
5. if (enemy_color == ENEMY_BLUE) { /* */
6. color_channel = channels[0]; /* 根据目标颜色进行通道提取 */
7. } else if (enemy_color == ENEMY_RED) { /* */
8. color_channel = channels[2]; /************************/
9. }
知识点:
1、 Mat //opencv中独有,相当于C/C++中的int类型专用于储存图像
参考文档:Mat类型详解
2、 vector //可以理解为容器或动态数组
问题:
用cv::Mat channels; 定义和std::vectorcv::Mat channels;
定义的效果一样为什么不用cv::Mat channels;定义?
解答:
(1)输出数组为定长容器,而vector为变长容器
(2)vector可用.at()以避免a[-1]访问越界的问题
(3)vector还提供了.size()和.empty(),而输出数组只能用sizeof()/strlen()
(4)以及遍历计数来获取大小和是否为空
(5)vector()和array会自动释放。
参考文档:
https://www.cnblogs.com/HL-space/p/10546605.html
https://blog.csdn.net/weixin_41743247/article/details/90635931
3、函数: split(通道拆分) //用于RGB通道加权处理,我们用它分离出红蓝两个通道
函数原型:
C++: void split(const Mat& src, // 填我们需要进行分离的多通道数组
Mat* mvbegin //填输出数组或者输出的vector容器
);
C++: void split(InputArray m, // 填我们需要进行分离的多通道数组
OutputArrayOfArrays mv //填输出数组或者输出的vector容器
);
参考文档:https://blog.csdn.net/qq_35294564/article/details/81058226
1-2 阀值化(二值化)
描述: 图像二值化( ImageBinarization)就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的黑白效果的过程。在数字图像处理中,二值图像占有非常重要的地位,图像的二值化使图像中数据量大为减少,从而能凸显出目标的轮廓。相当于将输入图像中阈值外的像素全部置0,使得阈值内目标凸出,便于PC进行灯条识别。
上交大源码:
1. cv::Mat src_bin_light, src_bin_dim;
2. int light_threshold;
3. if(enemy_color == ENEMY_BLUE){
4. light_threshold = 225;
5. }else{
6. light_threshold = 200;
7. }
8. cv::threshold(color_channel, src_bin_light, light_threshold, 255, CV_THRESH_BINARY); // 二值化对应通道
9. if (src_bin_light.empty()) return false;
10. imagePreProcess(src_bin_light); // 开闭运算
11. cv::threshold(color_channel, src_bin_dim, 140, 255, CV_THRESH_BINARY); // 二值化对应通道
12. if (src_bin_dim.empty()) return false;
13. imagePreProcess(src_bin_dim); // 开闭运算
知识点:
1、函数:cv::threshold() //通道拆分后,再通过二值化将单通道内的目标凸现
函数原型:
double cv::threshold(InputArray src,
//源图像,可以为8位的灰度图,也可以为32位的彩色图像。
OutputArray dst,//输出图像
double thresh,//阈值
double maxval,//dst图像中最大值
int type //阈值类型
)
参考文档:https://blog.csdn.net/qq_37385726/article/details/82015545
2、问题:
为什么二值化阈值为maxValue_gary * 0.7 或225/200/140?
解答(未完全):
在上交大中使用两个不同的二值化阈值同时进行灯条提取,减少环境光照对二值化这个操作的影响。同时剔除重复的灯条,剔除冗余计算,即对两次找出来的灯条取交集。
3、 问题:
src_bin_light.empty()有什么用?
解答:
用于判断 .empty()前面的容器是否为空,若为空则置0
参考文档:.empty()的用法
1-3 开闭运算(腐蚀与膨胀)
描述: 通常,由于噪声的影响,图象在阈值化(二值化)后所得到边界往往是很不平滑的,物体区域具有一些噪声孔,背景区域上散布着一些小的噪声物体。连续的开和闭运算可以有效地改善这种情况。有时需要经过多次腐蚀之后再加上相同次数的膨胀,才可以产生比较好的效果。
上交大源码:
1. // 开闭运算,上交大采用的是先开运算->闭运算
2. static void imagePreProcess(cv::Mat &src) {
3. static cv::Mat kernel_erode = getStructuringElement(cv::MORPH_RECT, cv::Size(3, 5));
4. erode(src, src, kernel_erode);
5.
6. static cv::Mat kernel_dilate = getStructuringElement(cv::MORPH_RECT, cv::Size(3, 5));
7. dilate(src, src, kernel_dilate);
8.
9. static cv::Mat kernel_dilate2 = getStructuringElement(cv::MORPH_RECT, cv::Size(3, 5));
10. dilate(src, src, kernel_dilate2);
11.
12. static cv::Mat kernel_erode2 = getStructuringElement(cv::MORPH_RECT, cv::Size(3, 5));
13. erode(src, src, kernel_erode2);
14. }
知识点:
1、开运算与闭运算
(1)开运算: 腐蚀->膨胀
先腐蚀后膨胀的过程称为开运算。用来消除小物体、在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积。
(2)闭运算: 膨胀->腐蚀
先膨胀后腐蚀的过程称为闭运算。用来填充物体内细小空洞、连接邻近物体、平滑其边界的同时并不明显改变其面积。
参考文档:https://blog.csdn.net/caojinpei123/article/details/81916005
2、腐蚀与膨胀
函数:erode(腐蚀) //相当于减肥,将锚点以外的部分去除
函数原型:
void erode(
InputArra src,
OUtputArray dst,
InputArra kernel,
Point anchor=Point(-1,-1),
int iterations=1,
int borderType=BORDER_CONSTANT,
const Scalar& borderValue=morphologyDEfaultBoderValue()
);
函数:dilate(膨胀) //相当于增肥,将锚点以为的部分增加一圈像素点
函数原型:
void dilate(
InputArra src,
OUtputArray dst,
InputArra kernel,
Point anchor=Point(-1,-1),
int iterations=1,
int borderType=BORDER_CONSTANT,
const Scalar& borderValue=morphologyDEfaultBoderValue()
);
函数:getStructuringElement //该函数会返回指定形状和尺寸的结构元素
其实我对这个函数的用法也不是很清楚但是在《opencv3编程入门》中提到一般将这个函数与开闭运算配合使用,所以大家有需要的可以自行了解。
函数原型:
Mat getStructuringElement(int shape, //表示内核的形状,有三种形状可以选择
Size esize, //内核的尺寸
Point anchor = Point(-1, -1)//锚点的位置
);
//这里需要注意的是内核的尺寸好像一般用不到,可以直接省略。
1-4 轮廓检测
描述: 轮廓提取使用的是OpenCV自带的函数findContours具体原理可以百度(其实我业不太清楚)
上交源码:
1. std::vector<std::vector<cv::Point>> light_contours_light, light_contours_dim;
2. std::vector<cv::Vec4i> hierarchy_light, hierarchy_dim;
3. cv::findContours(src_bin_light, light_contours_light, hierarchy_light, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
4. cv::findContours(src_bin_dim, light_contours_dim, hierarchy_dim, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
知识点:
1、vector
参考文档:https://blog.csdn.net/Ahuuua/article/details/80593388
2、函数:findContours() //原理的话可以或者百度
函数原型:
void cv::findContours (
InputOutputArray image,
OutputArrayOfArrays contours,
OutputArray hierarchy,
int mode,
int method,
Point offset = Point()
)
参考文档:opencv学习(四十)之寻找图像轮廓findContours()
感谢你的认真阅读,如果觉得对你有帮助的话可以给我点个赞,谢谢!
交龙2019赛季步兵视觉代码详解