162 lines
7.5 KiB
C++
162 lines
7.5 KiB
C++
|
|
/*
|
|||
|
|
* ============================================================
|
|||
|
|
* 实验 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;
|
|||
|
|
}
|