feat: init OpenCV C++ training course with 8 experiments and detailed tutorial
This commit is contained in:
36
exp2_color_space/CMakeLists.txt
Normal file
36
exp2_color_space/CMakeLists.txt
Normal file
@@ -0,0 +1,36 @@
|
||||
# ============================================================
|
||||
# 实验 2:颜色空间转换 - CMake 构建配置文件
|
||||
# ============================================================
|
||||
|
||||
# 指定 CMake 最低版本要求(3.10 是一个广泛兼容的版本)
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
# 项目名称:exp2_color_space
|
||||
# 这个名称用于标识整个 CMake 项目
|
||||
project(exp2_color_space)
|
||||
|
||||
# 设置 C++ 标准为 C++11
|
||||
# C++11 引入了许多现代语言特性,使代码更简洁、更安全
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
# 强制要求编译器必须支持 C++11,否则停止配置
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# 查找并加载 OpenCV 库
|
||||
# REQUIRED 参数:如果找不到 OpenCV,CMake 会立即报错退出
|
||||
# 这一步会设置 ${OpenCV_LIBS} 和 ${OpenCV_INCLUDE_DIRS} 等变量
|
||||
find_package(OpenCV REQUIRED)
|
||||
|
||||
# 定义可执行文件目标
|
||||
# 可执行文件名:exp2(运行命令为 ./exp2)
|
||||
# 源文件:main.cpp
|
||||
add_executable(exp2 main.cpp)
|
||||
|
||||
# 将 OpenCV 的动态库/静态库链接到 exp2 可执行文件
|
||||
# 没有这行代码,编译器会报告大量"未定义符号"错误
|
||||
# 因为 main.cpp 中调用的 OpenCV 函数实现都在这些库里
|
||||
target_link_libraries(exp2 ${OpenCV_LIBS})
|
||||
|
||||
# 将 OpenCV 的头文件目录添加到 include 路径
|
||||
# 使 #include <opencv2/opencv.hpp> 能被正确找到
|
||||
include_directories(${OpenCV_INCLUDE_DIRS})
|
||||
161
exp2_color_space/main.cpp
Normal file
161
exp2_color_space/main.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* ============================================================
|
||||
* 实验 2:颜色空间转换(BGR → Gray、HSV、Lab、YCrCb)
|
||||
* 知识点:cv::cvtColor
|
||||
* ============================================================
|
||||
*
|
||||
* 【实验目标】
|
||||
* 1. 理解"颜色空间"的概念 —— 同一张图片的不同"坐标系"描述
|
||||
* 2. 掌握 cv::cvtColor 函数的使用方法
|
||||
* 3. 了解 Gray、HSV、Lab、YCrCb 各自的特点和用途
|
||||
*
|
||||
* 【生活化比喻】
|
||||
* "颜色空间"就像是描述一个人位置的不同方式:
|
||||
* - BGR 像"经纬度坐标"(给出精确的三维数值)
|
||||
* - Gray 像"海拔高度"(一维数值,只有亮度)
|
||||
* - HSV 像"方向+距离+高度"(色相、饱和度、明度)
|
||||
* 色相(H) :是什么颜色(红/黄/绿/蓝...),像钟表上的角度
|
||||
* 饱和度(S):颜色有多"纯"(鲜艳还是发灰),像果汁的浓度
|
||||
* 明度(V) :颜色有多亮(亮还是暗),像灯光的亮度
|
||||
* - 不同场景用不同描述方式更方便!
|
||||
*
|
||||
* RoboMaster 任务中,HSV 是最常用的颜色空间
|
||||
* 因为它把"颜色"和"亮度"分开了,不受光照变化的影响
|
||||
*/
|
||||
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <iostream>
|
||||
#include <vector> // std::vector —— 动态数组,用于生成渐变色块
|
||||
|
||||
int main()
|
||||
{
|
||||
// ==========================================
|
||||
// 第一部分:生成一张色彩丰富的测试图片
|
||||
// ==========================================
|
||||
std::cout << "[步骤 1] 正在生成彩色测试图片..." << std::endl;
|
||||
|
||||
// 创建一个 300×600 的彩色画布,背景为黑色
|
||||
cv::Mat color_image(300, 600, CV_8UC3, cv::Scalar(0, 0, 0));
|
||||
|
||||
// 画出 6 个彩色矩形,代表不同颜色
|
||||
// 颜色顺序:红、绿、蓝、黄、青、品红
|
||||
// 每个矩形宽 100 像素,高 300 像素
|
||||
std::vector<cv::Scalar> colors = {
|
||||
cv::Scalar(0, 0, 255), // 红色 (B=0, G=0, R=255)
|
||||
cv::Scalar(0, 255, 0), // 绿色 (B=0, G=255, R=0)
|
||||
cv::Scalar(255, 0, 0), // 蓝色 (B=255, G=0, R=0)
|
||||
cv::Scalar(0, 255, 255), // 黄色 (B=0, G=255, R=255)
|
||||
cv::Scalar(255, 255, 0), // 青色 (B=255, G=255, R=0)
|
||||
cv::Scalar(255, 0, 255) // 品红色 (B=255, G=0, R=255)
|
||||
};
|
||||
|
||||
// 用循环依次画出每个色块
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
// 每个矩形宽 100 像素,所以左上角 x = i * 100
|
||||
int x_start = i * 100;
|
||||
cv::rectangle(color_image,
|
||||
cv::Point(x_start, 0), // 左上角
|
||||
cv::Point(x_start + 100, 300), // 右下角
|
||||
colors[i], // 填充颜色
|
||||
-1); // -1 = 填充
|
||||
}
|
||||
|
||||
// 在每个色块上写颜色名称
|
||||
std::vector<std::string> color_names = {"Red", "Green", "Blue", "Yellow", "Cyan", "Magenta"};
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
cv::putText(color_image, color_names[i],
|
||||
cv::Point(i * 100 + 10, 150),
|
||||
cv::FONT_HERSHEY_SIMPLEX, 0.6,
|
||||
cv::Scalar(255, 255, 255), // 白色文字
|
||||
2);
|
||||
}
|
||||
|
||||
// 保存原始彩色图
|
||||
cv::imwrite("original.jpg", color_image);
|
||||
std::cout << " -> 原始彩色图已保存为 original.jpg" << std::endl;
|
||||
|
||||
// ==========================================
|
||||
// 第二部分:执行各种颜色空间转换
|
||||
// ==========================================
|
||||
|
||||
// --- 转换 1:BGR → 灰度 (Gray) ---
|
||||
// 灰度图像只有一个通道,每个像素的取值范围是 0~255
|
||||
// 转换公式(心理学灰度):
|
||||
// Gray = 0.299 × R + 0.587 × G + 0.114 × B
|
||||
// 人眼对绿色最敏感(权重 0.587),对蓝色最不敏感(权重 0.114)
|
||||
std::cout << "[步骤 2] BGR -> Gray(灰度)..." << std::endl;
|
||||
cv::Mat gray_image;
|
||||
cv::cvtColor(color_image, gray_image, cv::COLOR_BGR2GRAY);
|
||||
cv::imwrite("gray.jpg", gray_image);
|
||||
std::cout << " -> 已保存为 gray.jpg(单通道灰度图)" << std::endl;
|
||||
|
||||
// --- 转换 2:BGR → HSV ---
|
||||
// HSV 三分量:
|
||||
// H (Hue,色相) :0~180(OpenCV 中缩放到此范围,实际是 0~360 度)
|
||||
// S (Saturation,饱和度):0~255(越大越鲜艳,0 就是灰色)
|
||||
// V (Value,明度) :0~255(越大越亮)
|
||||
std::cout << "[步骤 3] BGR -> HSV..." << std::endl;
|
||||
cv::Mat hsv_image;
|
||||
cv::cvtColor(color_image, hsv_image, cv::COLOR_BGR2HSV);
|
||||
cv::imwrite("hsv.jpg", hsv_image);
|
||||
std::cout << " -> 已保存为 hsv.jpg(注意:HSV 图片直接查看颜色会怪怪的,"
|
||||
<< "因为查看器把它当 BGR 解释了)" << std::endl;
|
||||
|
||||
// --- 转换 3:BGR → Lab ---
|
||||
// Lab 是设备无关的颜色空间,设计目标是接近人类视觉感知
|
||||
// L (Lightness,亮度) :0~100(实际 OpenCV 映射到 0~255)
|
||||
// a (绿-红轴) :负值偏绿,正值偏红
|
||||
// b (蓝-黄轴) :负值偏蓝,正值偏黄
|
||||
std::cout << "[步骤 4] BGR -> Lab..." << std::endl;
|
||||
cv::Mat lab_image;
|
||||
cv::cvtColor(color_image, lab_image, cv::COLOR_BGR2Lab);
|
||||
cv::imwrite("lab.jpg", lab_image);
|
||||
std::cout << " -> 已保存为 lab.jpg" << std::endl;
|
||||
|
||||
// --- 转换 4:BGR → YCrCb ---
|
||||
// YCrCb 常用于图像/视频压缩(如 JPEG、MPEG)
|
||||
// Y (亮度分量) :类似灰度
|
||||
// Cr (红色色度分量) :红色与亮度的差异
|
||||
// Cb (蓝色色度分量) :蓝色与亮度的差异
|
||||
// 人眼对亮度敏感,对色度不敏感,所以压缩时可以对 CbCr 做更多压缩
|
||||
std::cout << "[步骤 5] BGR -> YCrCb..." << std::endl;
|
||||
cv::Mat ycrcb_image;
|
||||
cv::cvtColor(color_image, ycrcb_image, cv::COLOR_BGR2YCrCb);
|
||||
cv::imwrite("ycrcb.jpg", ycrcb_image);
|
||||
std::cout << " -> 已保存为 ycrcb.jpg" << std::endl;
|
||||
|
||||
// ==========================================
|
||||
// 第三部分:演示 HSV 各通道的分离
|
||||
// ==========================================
|
||||
std::cout << "[步骤 6] 分离 HSV 各通道..." << std::endl;
|
||||
|
||||
// cv::split(输入图像, 输出通道数组) —— 将多通道图像拆分成多个单通道图像
|
||||
// 例如 3 通道的 HSV 图像可以拆成 H、S、V 三张独立的灰度图
|
||||
// std::vector<cv::Mat> 是一个能存放多个 cv::Mat 的动态数组
|
||||
std::vector<cv::Mat> hsv_channels;
|
||||
cv::split(hsv_image, hsv_channels);
|
||||
// 拆分后:
|
||||
// hsv_channels[0] = H 通道(色相)
|
||||
// hsv_channels[1] = S 通道(饱和度)
|
||||
// hsv_channels[2] = V 通道(明度)
|
||||
|
||||
// 分别保存三个通道
|
||||
cv::imwrite("hsv_channel_H.jpg", hsv_channels[0]); // 色相通道
|
||||
cv::imwrite("hsv_channel_S.jpg", hsv_channels[1]); // 饱和度通道
|
||||
cv::imwrite("hsv_channel_V.jpg", hsv_channels[2]); // 明度通道
|
||||
std::cout << " -> 已保存 hsv_channel_H.jpg / S.jpg / V.jpg" << std::endl;
|
||||
|
||||
// 总结
|
||||
std::cout << std::endl;
|
||||
std::cout << "===== 实验 2 完成 =====" << std::endl;
|
||||
std::cout << "你学会了:" << std::endl;
|
||||
std::cout << " 1. cv::cvtColor() 可以在不同颜色空间之间转换" << std::endl;
|
||||
std::cout << " 2. BGR → Gray:加权平均得到亮度" << std::endl;
|
||||
std::cout << " 3. BGR → HSV:分开颜色和亮度,对光照变化更鲁棒" << std::endl;
|
||||
std::cout << " 4. cv::split() 把多通道图像拆成单通道" << std::endl;
|
||||
std::cout << " 5. RoboMaster 中 HSV 是核心!后面实验 7 会用到" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user