Files

162 lines
7.5 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* ============================================================
* 实验 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;
}