OpenCV 30 讲学习总结:从零基础到机器人视觉

本文系统梳理 OpenCV C++ 课程的核心知识点,涵盖色彩空间转换、Mat 对象、像素操作、归一化、图像缩放与插值、翻转、视频读写、卷积、高斯模糊及人脸检测。所有代码均来自实际项目 QuickDemo 类,已在 Visual Studio + OpenCV 4.4+ 环境下验证通过,并附常见编译与运行错误解决方案。


一、课程内容筛选(按机器人视觉需求)

根据机器人 + YOLO 的目标,将 30 讲分为三类:

类型 章节 说明
精学 图像读写、色彩空间转换、Mat 对象、归一化、图像缩放、摄像头读取、卷积 高频操作,必须熟练掌握
过一遍 像素遍历、通道分离、像素统计、翻转、旋转、视频保存、高斯模糊、人脸检测 理解流程,用时能查
跳过 滚动条、键盘鼠标交互、绘图、直方图等 与机器人感知关系不大

二、图像窗口与显示基础

  • namedWindow("win", WINDOW_FREERATIO) 允许自由调整窗口比例。
  • waitKey(0) 无限等待按键;waitKey(30) 等待 30 毫秒,常用于视频循环。
  • 图像深度:24 位(3 通道 × 8 位/通道),像素值范围 0~255。
cpp 复制代码
// 对应 QuickDemo 中 colorSpace_Demo 等函数的开头
Mat src = imread("D:/test.jpg", IMREAD_COLOR);
if (src.empty()) return -1;
namedWindow("输入", WINDOW_FREERATIO);
imshow("输入", src);
waitKey(0);
destroyAllWindows();

三、Mat 对象创建与赋值

  • Mat 包含头部(尺寸、类型、通道)、数据指针、引用计数。
  • 深拷贝:clone()copyTo(),独立内存。
  • 浅拷贝:直接赋值,共享数据。
cpp 复制代码
// 来自 QuickDemo::mat_creation_demo
Mat m3 = Mat::zeros(Size(8, 8), CV_8UC3);
m3 = Scalar(0, 0, 255);          // 红色
cout << "width: " << m3.cols << " height: " << m3.rows << " channels: " << m3.channels() << endl;

Mat m4;
m3.copyTo(m4);
m4 = Scalar(0, 255, 255);        // 黄色,不影响 m3
imshow("图像", m3);
imshow("图像4", m4);

四、像素读写操作

  • 灰度图:image.at<uchar>(row, col)
  • 彩色图:image.at<Vec3b>(row, col)[0](0=B,1=G,2=R)
  • 指针遍历效率更高(参考 pixel_visit_demo):
cpp 复制代码
// 来自 QuickDemo::pixel_visit_demo(指针版本)
int w = image.cols, h = image.rows, dims = image.channels();
for (int row = 0; row < h; row++) {
    uchar* current_row = image.ptr<uchar>(row);
    for (int col = 0; col < w; col++) {
        if (dims == 1) { // 灰度图
            int pv = *current_row;
            *current_row++ = 255 - pv;
        }
        if (dims == 3) { // 彩色图
            *current_row++ = 255 - *current_row;
            *current_row++ = 255 - *current_row;
            *current_row++ = 255 - *current_row;
        }
    }
}
imshow("像素读写演示", image);

五、通道分离与合并

cpp 复制代码
// 来自 QuickDemo::channels_demo
vector<Mat> mv;
split(image, mv);
imshow("蓝色", mv[0]);
imshow("绿色", mv[1]);
imshow("红色", mv[2]);

Mat dst;
mv[0] = 0;
// mv[1] = 0;
merge(mv, dst);
imshow("蓝色通道置零", dst);

int from_to[] = { 0, 2, 1, 1, 2, 0 };
mixChannels(&image, 1, &dst, 1, from_to, 3);
imshow("通道重映射", dst);

六、色彩空间转换(重点,位于归一化之前)

转换函数 cvtColor(src, dst, code),常用常量值:

常量 数值 说明
COLOR_BGR2GRAY 6 彩色 → 灰度
COLOR_GRAY2BGR 8 灰度 → 伪彩色(无法恢复真彩色)
COLOR_BGR2HSV 40 BGR → HSV
COLOR_HSV2BGR 54 HSV → BGR
COLOR_BGR2RGB 4 通道交换

HSV 范围(OpenCV 特有)

  • H: 0 ~ 180(色调)
  • S: 0 ~ 255(饱和度)
  • V: 0 ~ 255(明度)

HSV 空间在颜色分割时更稳定,不受光照强度影响。

cpp 复制代码
// 来自 QuickDemo::colorSpace_Demo
Mat gray, hsv;
cvtColor(image, hsv, COLOR_BGR2HSV);
cvtColor(image, gray, COLOR_BGR2GRAY);
imshow("HSV", hsv);
imshow("灰度", gray);
imwrite("D:/hsv.png", hsv);
imwrite("D:/gray.png", gray);

常见错误 :混淆 BGR 与 RGB 导致颜色异常,可用 cvtColor(src, dst, COLOR_BGR2RGB) 修正;HSV 中 H 值误用 0~360 导致分割失败。

6.1 提取指定色彩范围(inRange)与背景替换

cpp 复制代码
// 来自 QuickDemo::inrange_demo
Mat hsv;
cvtColor(image, hsv, COLOR_BGR2HSV);
Mat mask;
inRange(hsv, Scalar(35, 43, 46), Scalar(77, 255, 255), mask);
Mat redback = Mat::zeros(image.size(), image.type());
redback = Scalar(40, 40, 200);
bitwise_not(mask, mask);
imshow("mask", mask);
image.copyTo(redback, mask);
imshow("roi区域提取", redback);

七、图像归一化(深度学习前处理关键)

重要:归一化必须在色彩空间转换之后进行,因为神经网络通常要求输入为 0,1-1,1 范围的浮点数。

目的:将像素值从 0,255 映射到 0,1-1,1

cpp 复制代码
// 来自 QuickDemo::norm_demo
Mat dst;
cout << image.type() << endl;          // 16 (CV_8UC3)
image.convertTo(image, CV_32F);        // 改为浮点
cout << image.type() << endl;          // 21 (CV_32FC3)
normalize(image, dst, 1.0, 0, NORM_MINMAX);
cout << dst.type() << endl;            // 21
imshow("图像数据归一化", dst);

注意convertTo 只改变数据类型,不改变数值范围,必须再除以 255 或使用 normalize


八、图像放缩与插值

cpp 复制代码
// 来自 QuickDemo::resize_demo
Mat zoomin, zoomout;
int h = image.rows, w = image.cols;
resize(image, zoomin, Size(w/2, h/2), 0, 0, INTER_LINEAR);
imshow("zoomin", zoomin);
resize(image, zoomout, Size(w*1.5, h*1.5), 0, 0, INTER_LINEAR);
imshow("zoomout", zoomout);

插值算法(由快到慢、由低到高质量):

  • INTER_NEAREST:最近邻,速度快,有锯齿
  • INTER_LINEAR:双线性(默认),质量与速度平衡
  • INTER_CUBIC:双三次,较慢,质量更高
  • INTER_LANCZOS4:Lanczos 插值,质量最高但最慢

缩小图像时推荐 INTER_AREA


九、图像翻转与旋转

翻转

cpp 复制代码
// 来自 QuickDemo::flip_demo
Mat dst;
// flip(image, dst, 0); // 上下翻转
// flip(image, dst, 1); // 左右翻转
flip(image, dst, -1); // 180°旋转
imshow("图像翻转", dst);

旋转(仿射变换)

cpp 复制代码
// 来自 QuickDemo::rotate_demo
Mat dst, M;
int w = image.cols, h = image.rows;
M = getRotationMatrix2D(Point2f(w/2, h/2), 45, 1.0);
double cos = abs(M.at<double>(0,0));
double sin = abs(M.at<double>(0,1));
int nw = cos*w + sin*h;
int nh = sin*w + cos*h;
M.at<double>(0,2) += (nw/2 - w/2);
M.at<double>(1,2) += (nh/2 - h/2);
warpAffine(image, dst, M, Size(nw, nh), INTER_LINEAR, 0, Scalar(255,255,0));
imshow("旋转演示", dst);

十、摄像头与视频读取

cpp 复制代码
// 来自 QuickDemo::video_demo(读取部分)
VideoCapture capture("D:/images/video/example_dsh.mp4"); // 也可用 0 打开摄像头
int frame_width = capture.get(CAP_PROP_FRAME_WIDTH);
int frame_height = capture.get(CAP_PROP_FRAME_HEIGHT);
double fps = capture.get(CAP_PROP_FPS);
Mat frame;
while (true) {
    capture.read(frame);
    if (frame.empty()) break;
    imshow("frame", frame);
    if (waitKey(1) == 27) break;   // ESC 退出
}
capture.release();

十一、视频保存

cpp 复制代码
// 来自 QuickDemo::video_demo(保存部分)
VideoWriter writer("D:/test.mp4", capture.get(CAP_PROP_FOURCC), fps,
                   Size(frame_width, frame_height), true);
while (capture.read(frame)) {
    writer.write(frame);
}
writer.release();

常用 fourcc:'M','J','P','G'(MJPG)、'X','V','I','D'(XVID)。


十二、图像卷积(均值模糊)

卷积核在图像上滑动,每个位置做加权和。均值模糊核所有系数相等。

cpp 复制代码
// 来自 QuickDemo::blur_demo
Mat dst;
blur(image, dst, Size(15, 15), Point(-1, -1));
imshow("图像模糊", dst);
  • 核大小必须为奇数(1,3,5,...)
  • Point(-1,-1) 表示锚点位于核中心

十三、高斯模糊

高斯核权重符合正态分布,中心最大,离中心越远权重越小,能更好地保留边缘。

cpp 复制代码
// 来自 QuickDemo::gaussian_blur_demo
Mat dst;
GaussianBlur(image, dst, Size(0, 0), 15);   // sigma=15,核大小自动计算
imshow("高斯模糊", dst);

高斯函数公式(近似):

G(x,y) = \\frac{1}{2\\pi\\sigma\^2} e^{-\\frac{x^2+y^2}{2\\sigma^2}}

  • sigma 越大,图像越模糊
  • 核大小可设为 Size(0,0) 由 sigma 自动推导

十四、人脸检测(DNN 模块)

使用 OpenCV DNN 加载 TensorFlow 模型进行人脸检测。

cpp 复制代码
// 来自 QuickDemo::face_detection_demo
string root_dir = "D:/opencv-4.4.0/opencv/sources/samples/dnn/face_detector/";
dnn::Net net = dnn::readNetFromTensorflow(root_dir + "opencv_face_detector_uint8.pb",
                                          root_dir + "opencv_face_detector.pbtxt");
VideoCapture capture("D:/images/video/example_dsh.mp4");
Mat frame;
while (true) {
    capture.read(frame);
    if (frame.empty()) break;
    Mat blob = dnn::blobFromImage(frame, 1.0, Size(300,300),
                                  Scalar(104,177,123), false, false);
    net.setInput(blob);
    Mat probs = net.forward();   // 输出维度: 1x1xNx7
    Mat detectionMat(probs.size[2], probs.size[3], CV_32F, probs.ptr<float>());
    for (int i = 0; i < detectionMat.rows; i++) {
        float confidence = detectionMat.at<float>(i, 2);
        if (confidence > 0.5) {
            int x1 = static_cast<int>(detectionMat.at<float>(i, 3) * frame.cols);
            int y1 = static_cast<int>(detectionMat.at<float>(i, 4) * frame.rows);
            int x2 = static_cast<int>(detectionMat.at<float>(i, 5) * frame.cols);
            int y2 = static_cast<int>(detectionMat.at<float>(i, 6) * frame.rows);
            Rect box(x1, y1, x2 - x1, y2 - y1);
            rectangle(frame, box, Scalar(0,0,255), 2, 8, 0);
        }
    }
    imshow("人脸检测演示", frame);
    if (waitKey(1) == 27) break;
}

输出解析

  • 矩阵形状 1×1×N×7,N 为检测框数量
  • 每个框的 7 个值:[batch_id, class_id, confidence, x1, y1, x2, y2]
  • 坐标是归一化的,需乘以原图宽高

十五、常见错误与解决方案

错误现象 原因 解决方法
窗口一闪而过 缺少 waitKey(0) 添加 waitKey(0)
imread 失败 路径单反斜杠 \ 被转义 使用 D:/D:\\
LNK1104 无法打开 .exe 上次程序未关闭,进程占用 关闭图像窗口,任务管理器结束进程
LNK2005 main 重复定义 多个 .cpp 文件包含 main 从项目中排除多余文件
C4244 警告 float 转 int 可能丢数据 显式强制转换 (int)x1
C4819 编码警告 文件含有中文且非 UTF-8 删除中文注释或另存为 UTF-8 with BOM
人脸检测置信度低 阈值 >0.5 太高 调试时降低到 0.3
卷积核大小为偶数 OpenCV 要求奇数 使用 3,5,7 等奇数
归一化后显示全黑 浮点图像 0,1 直接保存 imshow 可显示,保存前转回 CV_8U
VS 重装后环境丢失 未使用属性表 创建 .props 属性表,新项目直接添加
inRange 提取颜色不准确 光照影响 动态调整阈值或先做直方图均衡化

十六、心得体会

  1. 学以致用:OpenCV 只是工具,重点是与 YOLO、ROS2 结合。不需要精通全部 API,但必须熟练掌握图像获取 → 预处理(色彩空间转换 + 归一化)→ 推理 → 后处理这一链条。
  2. 笔记习惯 :手写笔记记录关键函数和参数,比光看视频记忆更深刻。例如整理 cvtColor 常量和对应数值,随时查阅。
  3. HSV 比 RGB 更适合作颜色分割:对光照不敏感,机器人视觉中提取目标颜色非常实用。
  4. 调试技巧cout 输出尺寸、类型,imshow 分步观察中间结果(特别是 mask),遇到链接错误先检查进程占用。
  5. 属性表 :创建 .props 属性表后,新建项目配置 OpenCV 只需 5 秒,强烈推荐。
  6. 色彩空间转换必须在归一化之前:因为归一化通常是在最终输入网络前对像素值范围进行压缩,而颜色转换是改变数据语义,必须先完成。

十七、总结

通过本次学习,我掌握了:

  • 图像读取、显示、保存及窗口操作
  • 色彩空间转换(BGR↔GRAY/HSV)以及利用 inRange 进行颜色提取和背景替换
  • Mat 对象的深/浅拷贝、创建空白图像
  • 像素级读写(灰度与彩色)与通道分离合并
  • 图像归一化(convertTo + /255normalize
  • 几何变换(缩放、翻转、旋转)
  • 视频摄像头读取与保存
  • 图像卷积(均值模糊)与高斯模糊
  • 基于 DNN 模块的人脸检测(加载模型、解析输出、绘制结果)
  • 常见编译/运行错误及解决方案

接下来,我将把上述能力迁移到 ROS2 环境中,结合 cv_bridge 实现从 ROS 话题读取图像,并接入 YOLO 进行目标检测,最终完成实物机器人的视觉导航。

相关推荐
腾视科技AI2 小时前
安全驾驶 智在掌控|腾视科技ES06车载智能终端,为车辆运营赋能
大数据·人工智能·科技·安全·ai·边缘计算·车载智能终端
chsmiao2 小时前
深度学习之概率论
人工智能·深度学习·概率论
SEO_juper2 小时前
2026 五大高毛利细分赛道:关键词挖掘、建站模板、内容布局完整方案
大数据·人工智能·seo·geo·谷歌优化·2026·毛利
澹锦汐2 小时前
轻量化低代码一周交付:国外支付渠道集成实战细节
人工智能
LT10157974442 小时前
2026年RPA金融机器人,助力全品类金融运营升级全场景落地选型指南
金融·机器人·rpa
大霸王龙2 小时前
机器人维修工程师
人工智能·数据挖掘·机器人
SLD_Allen2 小时前
同花顺Skill广场,为金融AI实战注入新动能!
大数据·人工智能·金融
调试优选官2 小时前
2026上海生成式引擎优化公司业务:技术路线与服务能力图谱
大数据·人工智能·经验总结·技术分享·上海