【openCV】图像显示,色彩空间转换

目录

一.在VS2026里面配置openCV

二.图像读取与显示

2.1.基础示例

2.1.1.cv::imread函数

2.1.2.cv::imshow函数

2.1.3.cv::waitKey函数

[2.1.4.cv::destroyWindow 函数](#2.1.4.cv::destroyWindow 函数)

2.1.5.cv::destroyAllWindows函数

2.2.cv::namedWindow函数(图片自适应)

2.3.imread函数的第二个参数

2.3.1.灰度图

[2.3.2.透明通道(Alpha 通道)](#2.3.2.透明通道(Alpha 通道))

三.图像色彩空间转换

3.1.色彩空间是什么

[3.2.OpenCV 中的色彩空间转换](#3.2.OpenCV 中的色彩空间转换)


一.在VS2026里面配置openCV

首先,我们需要去这个opencv官网:OpenCV - Open Computer Vision Library

点进去之后,我们直接选则最新的版本进行下载

我们这里选则windows

下载完之后,我们直接双击打开

这个就是告诉你需要把它下载到哪里去,那么我们自己选择一个目录即可

接下来我告诉你

opencv的动态库是下面这两个

opencv的静态库是下面这些

我们就需要将动态库和静态库都烤出来,也就是下面这2个目录拷贝出来

我们就拷贝到下面这个目录里面来

但是有了库文件还不够,我们需要头文件,头文件其实都在下面这个目录里

那么我们直接把这个include目录给拷贝出来

同样的,也是拷贝到下面这个目录来

现在这个目录里就有了我们opencv的库文件和头文件了

现在我们就可以去写我们的代码来测试一下了

我们直接打开vs2026,创建一个空项目,然后就按照下面来

我们就打开了下面这样子的窗口

这里就有4种情况

配置 平台 说明
Debug x86 调试版本,32位程序,包含调试符号,未优化,便于断点、单步调试
Debug x64 调试版本,64位程序,包含调试符号,未优化,适合本地64位调试
Release x86 发布版本,32位程序,已优化,不包含调试信息,性能最佳
Release x64 发布版本,64位程序,已优化,适合最终发布和部署

这个其实在VS的下面这个状态栏就能看到

我们就根据自己的需要来进行配置

我们先以Debug x64环境为例来进行配置

取个名字

然后就会发现下面多了一个东西

我们去文件夹看看,其实也是生成了一个配置文件,我们只需要去修改这个配置文件,后面我们就可以直接套用这个配置文件,不需要再进行繁琐的配置了

我们去修改这个配置文件

我们先就来配置头文件路径

我们点击确定

接下来我们来配置一下库文件的路径

点击确认,接下来我们需要去指定链接目标

其实是链接下面哪一个库的

  • opencv_world4120.lib库是用于Release模式的
  • opencv_world4120d.lib是用于Debug模式的

我们这里是****Debug x64环境,所以我们选择填写 opencv_world4120d.lib

如果说你是Release环境,那么你就填写opencv_world4120.lib

点击确定,这样子就好了,现在我们编写一个opencv程序

cpp 复制代码
#include <opencv2/opencv.hpp>

int main() {
    cv::Mat img(400, 600, CV_8UC3, cv::Scalar(255, 255, 255)); // 白色背景
    cv::putText(img, "Hello OpenCV", cv::Point(100, 200), 
                cv::FONT_HERSHEY_SIMPLEX, 1.5, cv::Scalar(0, 0, 255), 2);
    cv::imshow("窗口", img);
    cv::waitKey(0);
    return 0;
}

我们在下面这种环境下运行

我们一运行,就会发现

这个是缺少了动态库的警告,还记得我们的动态库在哪里吗?

我们直接将这个动态库给拷贝过来,拷贝到哪里呢?点击下面这个打开文件

现在我们重新运行

非常完美。

如果说你觉得麻烦,那么我们也可以去借助环境变量的方式来进行配置动态库

然后把我们动态库所在目录配置进去就好了

这样子就好了。注意要重启一下VS


后续,如果说我们想要在新项目里面使用我们的配置,就直接添加现有的

再选择我们之前搞好的.props文件

这样子就OK了

窗口标题出现乱码

显示的图片窗口标题出现乱码,是因为 OpenCV 在 Windows 上默认使用 ANSI(本地编码,如 GBK)来处理字符串,而你的源代码文件(例如 VS 中新建的 .cpp 文件)很可能使用的是 UTF-8 编码(无 BOM)。两种编码不匹配,导致中文字符显示为乱码。

那么我们就可以来进行设置一下,去VS的高级保存选项设置成GBK编码即可

这样子就行了

二.图像读取与显示

2.1.基础示例

使用OPENCV显示一张图片,这个就是最基本的东西

cpp 复制代码
#include <opencv2/opencv.hpp>
#include <iostream>

int main() {
    cv::Mat img = cv::imread("Qt.png");

    if (img.empty()) {
        std::cout << "错误:无法加载图片,请检查路径!" << std::endl;
        return -1;
    }

    cv::imshow("图片", img);
    cv::waitKey(10000);  // 等待任意按键后关闭窗口
    return 0;
}

我们仔细看看这里面的几个函数

2.1.1.cv::imread函数

作用:读取图像文件,并将其解码为一个 OpenCV 的矩阵对象(cv::Mat)。

参数:

第一个参数(必需):图像文件的路径。这里是 "Qt.png"(相对路径,表示与可执行文件同目录下的 Qt.png 文件)。

第二个参数(可选):读取方式,用整数或枚举值指定。常见取值有:

  • cv::IMREAD_COLOR(默认值,等价于 1):加载彩色图像,忽略透明度(输出 3 通道 BGR)。
  • cv::IMREAD_GRAYSCALE(0):加载为灰度图(单通道)。
  • cv::IMREAD_UNCHANGED(-1):保留图像原样(包括 alpha 通道)。
  • 本代码省略了第二个参数,因此使用默认值 cv::IMREAD_COLOR。

返回值:cv::Mat 对象。如果读取失败(比如文件不存在、格式不支持),则返回一个空矩阵(img.empty() == true)。

2.1.2.cv::imshow函数

作用:在窗口中显示图像。窗口会自动调整大小以适应图像。

参数:

  • 第一个参数("图片"):窗口名称(字符串)。如果之前没有创建过同名窗口,OpenCV 会自动创建一个新窗口。
  • 第二个参数(img):要显示的图像(cv::Mat 对象)。

注意:**imshow 本身不会让窗口保留显示,它只是把图像交给窗口系统。**真正让窗口"停留"并刷新的是 cv::waitKey。

2.1.3.cv::waitKey函数

作用:等待用户按键事件,并同时处理窗口事件(刷新、重绘等)。没有它,图像窗口可能会一闪而过或完全不显示。

参数:

**延迟时间(毫秒)。**这里是 10000 毫秒,即 10 秒。

  • 若延时 > 0:等待指定毫秒,或在这期间按下任意键后立即返回。
  • 若延时 = 0:无限等待,直到用户按下任意键。
  • 若延时 < 0:不等按键,立即返回(很少用)。

返回值:

返回按下的按键的 ASCII 码(如果没有任何按键,则返回 -1)。本代码未使用返回值。

2.1.4.cv::destroyWindow 函数

作用:关闭并销毁指定名称的 OpenCV 窗口,释放与该窗口关联的系统资源(包括窗口句柄、内

部缓冲区等)。

它只有一个参数:**要销毁的窗口的名称。**该名称必须与之前调用 cv::namedWindow 或 cv::imshow 时使用的窗口名称完全一致(区分大小写)。

我们看一个简单的例子

cpp 复制代码
#include <opencv2/opencv.hpp>
#include <iostream>

int main() {
    // 1. 创建第一个画布(红色背景 + 文字)
    cv::Mat canvas1(300, 400, CV_8UC3, cv::Scalar(0, 0, 255)); // 红色
    cv::putText(canvas1, "Window 1 - I will be closed", 
                cv::Point(50, 150), 
                cv::FONT_HERSHEY_SIMPLEX, 
                0.7, cv::Scalar(255, 255, 255), 2);

    // 2. 创建第二个画布(绿色背景 + 文字)
    cv::Mat canvas2(300, 400, CV_8UC3, cv::Scalar(0, 255, 0)); // 绿色
    cv::putText(canvas2, "Window 2 - I will stay", 
                cv::Point(50, 150), 
                cv::FONT_HERSHEY_SIMPLEX, 
                0.7, cv::Scalar(0, 0, 0), 2);

    // 3. 显示两个窗口
    cv::imshow("Window 1", canvas1);
    cv::imshow("Window 2", canvas2);
    std::cout << "两个窗口已显示,按任意键关闭 Window 1..." << std::endl;

    // 4. 等待按键,然后关闭第一个窗口
    cv::waitKey(0);
    cv::destroyWindow("Window 1");   // 只关闭 Window 1
    std::cout << "Window 1 已关闭,Window 2 仍然存在。按任意键退出程序..." << std::endl;

    // 5. 再次等待按键,然后程序结束(第二个窗口会随进程结束自动销毁)
    cv::waitKey(0);
    std::cout << "程序结束,Window 2 自动关闭。" << std::endl;

    return 0;
}

我们点击左边红色的opencv窗口,点击回车

是不是很神奇!!!

2.1.5.cv::destroyAllWindows函数

OpenCV 还提供了一个批量销毁所有窗口的函数:cv::destroyAllWindows函数

  • 作用:销毁当前程序创建的所有 OpenCV 窗口(无论窗口名称是什么)。
cpp 复制代码
#include <opencv2/opencv.hpp>
#include <iostream>

int main() {
    // 创建第一个画布(红色背景 + 文字)
    cv::Mat canvas1(300, 400, CV_8UC3, cv::Scalar(0, 0, 255));
    cv::putText(canvas1, "Window 1", cv::Point(150, 150),
                cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(255, 255, 255), 2);

    // 创建第二个画布(绿色背景 + 文字)
    cv::Mat canvas2(300, 400, CV_8UC3, cv::Scalar(0, 255, 0));
    cv::putText(canvas2, "Window 2", cv::Point(150, 150),
                cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(0, 0, 0), 2);

    // 显示两个窗口
    cv::imshow("Window 1", canvas1);
    cv::imshow("Window 2", canvas2);
    std::cout << "两个窗口已显示。按任意键将调用 destroyAllWindows() 关闭所有窗口..." << std::endl;

    // 等待按键
    cv::waitKey(0);

    // 一次性销毁所有 OpenCV 窗口
    cv::destroyAllWindows();
    std::cout << "已调用 destroyAllWindows(),所有窗口均已关闭。" << std::endl;

    // 再等待一下,让用户看到控制台消息后按任意键退出程序
    std::cout << "按任意键退出程序..." << std::endl;
    std::cin.get();  // 或 cv::waitKey(0),但此时所有窗口已销毁,建议用控制台输入

    return 0;
}

这个就很简单了

2.2.cv::namedWindow函数(图片自适应)

现在我们放一张长度很长,宽度一般的图片,我们再使用上面那段代码打开,就会发现出现显示不全的问题

第一个参数:String类型,意义是窗口的唯一标识符(名称)。后续所有与该窗口有关的操作(如 imshow、destroyWindow 等)都要通过这个名字来引用它。

第二个参数:flags

  • 类型:int(整数,但通常使用预定义的枚举常量,可读性更好)。
  • 默认值:WINDOW_AUTOSIZE(即如果不写这个参数,或者写 0 或 1 等,OpenCV 会采用自动调整大小的行为)。
  • 作用:控制窗口的外观和用户交互方式。不同的 flags 值会决定窗口边框能否拖拽、内容是否随窗口缩放、是否启用 OpenGL、是否带工具栏等。

下面详细解释常用的 flags 取值及其效果:

WINDOW_NORMAL

  • 这个标志告诉 OpenCV:用户可以自由调整窗口的大小。你可以用鼠标拖拽窗口的边框或角落,把窗口拉大或缩小。
  • 当窗口大小改变时,内部显示的图像会被 OpenCV 自动缩放(使用插值算法)以适应新的窗口尺寸。这可能导致图像看起来变模糊或变形,但不会改变原始图像数据本身。
  • 适用于需要用户交互地改变显示尺寸的场景,比如图像查看器。
  • 如果图像非常大,使用 WINDOW_NORMAL 可以一开始就显示为缩小的视图,而不会撑满屏幕。

WINDOW_AUTOSIZE

  • 这是 默认 标志。 它的行为刚好相反:窗口大小完全由图像本身的尺寸决定,用户无法通过鼠标改变窗口的大小。
  • 如果图像是 800×600 像素,窗口就会被创建为 800×600 大小(不包括标题栏和边框)。如果图像尺寸超过屏幕分辨率,窗口可能超出屏幕范围,此时你可能需要移动窗口才能看到全部内容。
  • 优点:图像以 1:1 的原始像素显示,不会因缩放而失真。缺点:用户无法调整大小,不适合需要在不同屏幕尺寸下查看的场景。

我们看看这个WINDOW_NORMAL

cpp 复制代码
#include <opencv2/opencv.hpp>
#include <iostream>

int main() {
    cv::Mat img = cv::imread("long.jpg");
    if (img.empty()) {
        std::cout << "错误:无法加载图片,请检查路径!" << std::endl;
        return -1;
    }
    cv::namedWindow("Window1", cv::WINDOW_NORMAL);
    cv::imshow("Window1", img);//注意这里的窗口名称必须与上面的一致
    cv::waitKey(0);  // 等待任意按键后关闭窗口
    return 0;
}

我们可以去拉伸这个图片

2.3.imread函数的第二个参数

2.3.1.灰度图

  1. 先想想你见过的"黑白照片"或"黑白电视"

你看过老式的黑白照片吗?或者老电影里那种没有颜色的画面?

那种照片里,没有红色、没有蓝色、没有绿色------只有不同程度的灰色:有的地方很黑(比如头发、阴影),有的地方很白(比如雪地、亮光),中间还有浅灰、中灰、深灰等等。

灰度图就是这种"只有灰色,没有彩色"的图片。

  1. 把彩色世界想象成"画画"
  • 彩色图片就像你用一盒彩色蜡笔画出来的画:你可以用红色画太阳,用绿色画草地,用蓝色画天空。

  • 灰度图就像你只用一支铅笔 (或者一根黑色/灰色的蜡笔)来画:你通过用力重一点 画出深黑色,轻轻画 画出浅灰色,不画就留白成白色。整幅画只有黑、白、以及中间的各种灰色,没有别的颜色。

  1. 为什么要有灰度图?

因为对于很多事情,我们其实不需要知道颜色 ,只需要知道"亮不亮"、"暗不暗"。

比如:

  • 你想识别一张照片里有没有:猫的形状、耳朵、眼睛、胡子,这些信息跟它是黄猫还是黑猫没关系。你把彩色照片变成灰度图,猫还是那个形状,但数据变小了(电脑处理起来快很多)。

  • 你想把一篇文章扫描进电脑:文字是黑是白比它是什么颜色重要,灰度图足够用了。

  1. 灰度图在电脑里的样子

电脑里,灰度图的每一个"点"(像素)只存一个数字:

  • 0 表示纯黑色

  • 255 表示纯白色

  • 中间的数字(比如 128)表示中灰色(一半白一半黑的感觉)

你可以把灰度图理解成一张由很多小格子组成的图,每个格子要么黑、要么白、要么某种灰色。就像填字游戏里每个格子只涂一种"灰度颜料",没有红绿蓝。


那,我们要怎么才能显示灰度图呢?

这个其实是和cv::imread函数的第二个参数有问题

作用:读取图像文件,并将其解码为一个 OpenCV 的矩阵对象(cv::Mat)。

参数:

第一个参数(必需):图像文件的路径。这里是 "Qt.png"(相对路径,表示与可执行文件同目录下的 Qt.png 文件)。

第二个参数(可选):读取方式,用整数或枚举值指定。常见取值有:

  • cv::IMREAD_GRAYSCALE:加载为灰度图(单通道)。
cpp 复制代码
#include <opencv2/opencv.hpp>
#include <iostream>

int main() {
    cv::Mat img = cv::imread("Qt.png", cv::IMREAD_GRAYSCALE);
    if (img.empty()) {
        std::cout << "错误:无法加载图片,请检查路径!" << std::endl;
        return -1;
    }
    cv::imshow("Window1", img);//注意这里的窗口名称必须与上面的一致
    cv::waitKey(0);  // 等待任意按键后关闭窗口
    return 0;
}

这样子就很OK了

2.3.2.透明通道(Alpha 通道)

  1. 先回忆一下"通道"是什么
  • 一张彩色图片(比如常见的 RGB 图片)可以看作由三张"单色图"叠在一起:一张记录红色有多少、一张记录绿色有多少、一张记录蓝色有多少。这三张就是三个通道

  • 灰度图只有一张"亮度图",所以是一个通道

  1. 透明通道是"第四张图"

透明通道(通常叫 Alpha 通道,记作 A)就像是额外附加的一张灰度图 ,但它记录的不是颜色亮度,而是每个像素的"不透明程度"

  • 0 表示完全透明(看不见,像玻璃一样透过去)

  • 255 表示完全不透明(实心的,挡住后面的东西)

  • 中间的数字(如 128)表示半透明(像毛玻璃或半透明的塑料纸,后面的东西能透过来一些)

所以,一张带透明通道的彩色图像实际上是 四个通道:R(红)、G(绿)、B(蓝)、A(透明)。

  1. 用贴纸和玻璃纸来比喻

想象你手里有一张印着卡通人物的贴纸

  • 卡通人物本身是完全不透明的(你看不到贴纸背面的东西) → 相当于 Alpha = 255。

  • 贴纸空白的地方是完全透明的(你可以透过空白处看到下面的桌面) → 相当于 Alpha = 0。

  • 如果你用一张半透明的磨砂玻璃纸盖在照片上,玻璃纸本身让照片模糊地透过来 → 相当于 Alpha = 某个中间值(比如 128)。

透明通道就是告诉你:这张图的每个点,到底有多"实心",有多"透光"。

  1. 生活中哪里用到透明通道?
  • PNG 图片:很多网站的 Logo、图标(比如微信的绿色对话气泡图标)背景是透明的,这样无论放到什么背景色上都不会有一个白色方块框住它。这种透明就是用 Alpha 通道实现的。

  • PPT 里的半透明形状:你可以在 PowerPoint 里画一个圆形,把填充透明度设为 50%,这样它会半透明地盖住背后的文字。这个 50% 就是 Alpha 值。

  • 视频特效:电影里的"火焰"、"爆炸"素材常常是带透明通道的,方便合成到其他画面上。

  1. 透明通道 vs 灰度图
  • 灰度图本身是一张完整的图像,你可以单独看到它(一张黑白照片)。

  • 透明通道不能单独作为一张"图像"来看(虽然它也是灰度值),它必须依附于一张彩色图像,用来控制这张彩色图像的哪些部分透明、哪些不透明

你可以在 Photoshop 里打开一个带透明区域的 PNG 图片,然后切换到"通道"面板,就会看到四个通道:红、绿、蓝、以及一个叫"Alpha"的通道。点开 Alpha 通道,你会发现它显示为一张黑白图------白色区域表示不透明,黑色区域表示完全透明,灰色表示半透明

  1. 为什么需要透明通道?

没有透明通道时(比如普通的 JPG 图片),图片永远是矩形 的,即使你画了一个圆形的人物,它的背景也是某种颜色(通常是白色或黑色),放到另一个背景上就会很突兀。

有了透明通道,你就可以把人物"抠"出来,让背景完全透明,然后自由地合成到任何其他图片上,就像把贴纸撕下来贴到别的地方一样。


那么我们的图片如果有通信通道,那么我们就需要将它加载出来

同样是imread的第二个参数:读取方式,用整数或枚举值指定。常见取值有:

  • cv::IMREAD_UNCHANGED(-1):保留图像原样(包括 alpha 通道)。
cpp 复制代码
#include <opencv2/opencv.hpp>
#include <iostream>

int main() {
    cv::Mat img = cv::imread("Qt.png", cv::IMREAD_UNCHANGED);
    if (img.empty()) {
        std::cout << "错误:无法加载图片,请检查路径!" << std::endl;
        return -1;
    }
    cv::imshow("Window1", img);//注意这里的窗口名称必须与上面的一致
    cv::waitKey(0);  // 等待任意按键后关闭窗口
    return 0;
}

这个还是非常简单的

三.图像色彩空间转换

3.1.色彩空间是什么

阳光通过三棱镜会分解成红、橙、黄、绿、蓝、靛、紫------这是连续的物理光谱。但是计算机和人类不可能存储无穷多种颜色,所以我们需要一种有限的、数字化的方式来描述颜色。

色彩空间就是一套规则,它规定了:

  • 用哪几个"基本量"来描述颜色(比如用三个数字)
  • 这三个数字分别代表什么物理含义
  • 所有可能的颜色如何用这三个数字的组合来表示

你可以把色彩空间想象成三维坐标系,就像空间中的点用 (x, y, z) 表示一样,颜色也用三个坐标值来表示。

我们举一个例子来:

  • 例子1:RGB 色彩空间

RGB 是"红(Red)、绿(Green)、蓝(Blue)"的缩写。

原理:通过三种颜色的光按不同强度混合,可以产生几乎所有人眼能看到的颜色。你的手机屏幕、电脑显示器就是通过红、绿、蓝三个小灯来显示所有颜色的。

表示法:每个颜色用一个三元组 (R, G, B) 表示,通常每个分量范围 0~255(0 表示没有该颜色,255 表示该颜色最强)。

  • (255,0,0) 表示纯红
  • (0,255,0) 表示纯绿
  • (0,0,255) 表示纯蓝
  • (255,255,255) 表示白色
  • (0,0,0) 表示黑色
  • (128,128,128) 表示中灰色

所以当你拍一张彩色照片,计算机存储的是每个像素的 (R,G,B) 三个数字。这就是RGB 色彩空间。

  • 例子2:灰度色彩空间

灰度色彩空间只有一个数字:亮度。0 表示黑,255 表示白,中间的数字表示不同深浅的灰色。

所以灰度图只有亮度信息,没有颜色信息。

  • 例子3:HSV 色彩空间

HSV 是色调(Hue)、饱和度(Saturation)、明度(Value)的缩写。

  • 色调(H):表示是什么颜色,用角度 0°~360° 表示,0° 红色,120° 绿色,240° 蓝色等。
  • 饱和度(S):表示颜色浓淡,0% 是灰色,100% 是纯彩色。
  • 明度(V):表示颜色有多亮,0% 是黑色,100% 是最亮。

HSV 更符合人类直觉:比如你想调一个"更红的颜色",你会改变色调;想让它"更鲜艳",就增加饱和度;想让它"更亮",就增加明度。

什么是"转换"?

转换就是:已知某个像素在一种色彩空间下的坐标值(比如 RGB 值),通过一个数学公式,计算出它在另一种色彩空间下的坐标值(比如 HSV 值)。

因为两个色彩空间描述的是同一个颜色,只是用了不同的语言。就像你手里有一杯水:你可以说它的体积是 500 毫升,也可以说它是 0.5 升。数字变了,但水量没变。同样,颜色没变,只是表达方式变了。

举例:RGB 转灰度

有一个很经典的公式:

cpp 复制代码
灰度 = 0.299 × R + 0.587 × G + 0.114 × B

比如一个像素的 RGB 是 (255, 100, 50)(很亮的红橙色)。

计算:0.299×255 ≈ 76.2,0.587×100 = 58.7,0.114×50 = 5.7,总和 ≈ 140.6。

四舍五入后灰度值 141。

所以这个像素在灰度空间里是 141(中等偏亮的灰色)。

这个转换就是色彩空间转换的一个具体例子。

三、为什么要做这种转换?(重点)

既然图像本来已经是 RGB 了,为什么还要转成别的?因为不同的图像处理任务在不同的色彩空间里做起来更简单、更准确。

场景1. 想把红色变得更红,但不改变亮度

在 RGB 空间里,红色是 (255,0,0)。你想让它"更红",只能把红色分量增大------但最大值已经是 255 了。而且你稍微改一下三个数字,亮度也会跟着变,很难控制。

显然我们就是不能在RGB图像色彩空间里面进行调整的,我们需要转换到HSV图像色彩空间去。

HSV 空间里,颜色、饱和度、亮度是三个独立的分量。

  • 色调(H)决定是什么颜色(红、绿、蓝...)

  • 饱和度(S)决定颜色浓不浓

  • 明度(V)决定亮不亮

你只要增加饱和度 S ,颜色就会变得更浓、更鲜艳,而不会改变它是红色还是亮度。如果你想保持亮度只改颜色,那就改 H。分开控制,简单直接。

我们如果想要调整饱和度,就先将图像色彩空间从RGB转换成HSV,调整完饱和度了之后,我们再转换回我们的RGB图像色彩空间。

场景2:想把彩色照片变成黑白效果

  • 不需要手动调整每个像素的RGB值,只需将图像从 RGB 色彩空间转换为灰度(GRAY)色彩空间即可。
  • 很多滤镜就是这样做的。

场景3:改变照片的"色调"而不改变亮度

  • 在 RGB 里,你想把整张照片变得偏红一些,你会把每个像素的 R 分量增加。但这样做同时也会改变亮度(因为红色更亮了)。
  • 在 HSV 里,亮度是单独一个通道 V。你只需要改变 H(色调)通道,保持 S 和 V 不变,就能改变颜色而保持亮度完全不变。这样调色效果更自然。

场景4:颜色识别/物体跟踪

  • 比如你想写一个程序追踪一个红色小球。在 RGB 空间里,"红色"没有一个简单的范围,因为受到光线亮度影响,红色可能看起来像粉色、暗红、橙色等。
  • 但在 HSV 空间里,红色对应 H 值大约 0~10 和 160~180,而且你可以过滤掉太暗(V 太低)或太灰(S 太低)的像素,就能更稳定地找到红色物体。

场景5:图像压缩(视频、JPEG)

  • 视频编码常用 YCbCr 色彩空间:Y 是亮度,Cb/Cr 是蓝色和红色的色差。因为人眼对亮度细节敏感,对颜色细节不敏感,可以大幅压缩颜色信息而不易察觉。
  • 这也是为什么很多格式需要从 RGB 转到 YCbCr。

3.2.OpenCV 中的色彩空间转换

OpenCV 提供了 cv::cvtColor() 函数,专门做这件事。

第一个参数:输入图像,cv::Mat类型

第二个参数:输出图像,cv::Mat类型

第三个参数:转换类型(或称为转换代码),是一个整数常量,用来指定从哪种色彩空间转换到哪种色彩空间。OpenCV 在 cv::ColorConversionCodes 枚举中定义了数百种转换代码,最常见的是 COLOR_BGR2GRAY、COLOR_BGR2HSV、COLOR_HSV2BGR、COLOR_BGR2RGB 等。

作用:告诉函数使用哪个数学公式来重新计算每个像素的颜色值。它是整个转换的核心指令。

命名规则:通常形式为 COLOR_<源空间>2<目标空间>,例如:

  • COLOR_BGR2GRAY:BGR 彩色 → 灰度
  • COLOR_GRAY2BGR:灰度 → BGR(输出三个通道,每个通道的值都等于原灰度值)
  • COLOR_BGR2HSV:BGR → HSV
  • COLOR_HSV2BGR:HSV → BGR

BGR 和 RGB 是两种不同的通道顺序,不是同一个东西。不过因为它们在数值上很像(都是三个通道,只是排列顺序不同),所以很容易被误认为是一回事。它们本质的区别

  • RGB:第一个分量是红色(R),第二个是绿色(G),第三个是蓝色(B)。
  • BGR:第一个分量是蓝色(B),第二个是绿色(G),第三个是红色(R)。

对于同一个颜色(比如纯红色),RGB 存为 (255, 0, 0),而 BGR 存为 (0, 0, 255)。数字完全不同,如果不做转换,直接按 RGB 去读 BGR 的数据,红色就会变成蓝色。

在 OpenCV 里,你用 imshow 显示图像时,它自动帮你把 BGR 转成了屏幕能正确显示的颜色,所以你看到的颜色是正常的,感觉不到区别。

示例

cpp 复制代码
#include <opencv2/opencv.hpp>
#include <iostream>

class QuickDemo
{
public:
    void colorSpace_Demo(cv::Mat& image)
    {
        cv::Mat gray, hsv;
        cv::cvtColor(image, hsv, cv::COLOR_BGR2HSV);
        cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
        cv::imshow("HSV", hsv);
        cv::imshow("灰度", gray);
        cv::imwrite("D:/hsv.png", hsv);
        cv::imwrite("D:/gray.png", gray);
    }
};

int main() {
    cv::Mat img = cv::imread("Qt.png", cv::IMREAD_UNCHANGED);
    if (img.empty()) {
        std::cout << "错误:无法加载图片,请检查路径!" << std::endl;
        return -1;
    }
    QuickDemo qd;
    qd.colorSpace_Demo(img);
    cv::imshow("Window1", img);//注意这里的窗口名称必须与上面的一致
    cv::waitKey(0);  // 等待任意按键后关闭窗口
    return 0;
}

我们这里使用了cv::imwrite,它其实是有3个参数在里面的,但是最常用的就是前两个参数

cv::imwrite 的前两个参数

第1个参数:filename

是什么:要保存的文件名,带路径(字符串)。

决定格式:OpenCV 根据文件名的扩展名(如 .png、.jpg、.bmp)自动选择保存格式。

例子:

  • "result.png" → 保存为 PNG 格式
  • "photo.jpg" → 保存为 JPEG 格式
  • "C:/images/out.bmp" → 保存为 BMP 格式

第2个参数:img

  • 是什么:你要保存的图像数据,就是 cv::Mat 类型的对象。
  • 要求:图像必须非空(已经成功加载或创建了内容)。
  • 注意:OpenCV 默认使用 BGR 颜色顺序,但当你保存为 .jpg 或 .png 等常见格式时,OpenCV 内部会自动转换成正确的颜色,所以你直接保存即可,不需要额外转换。

我们去看看保存的情况,和我们这里显示的是一样的。

相关推荐
海兰7 小时前
从原始日志到系统知识:补齐 AI 可观测性的“上下文层“
人工智能·elasticsearch
爱喝水的木子7 小时前
LearnPilot AI
人工智能
完成大叔7 小时前
从脚本到Agent:工具模式下的智能价值
人工智能·langchain
weixin_436182427 小时前
工业 AI 芯片如何选型?告别纸质手册,实现快速比对
人工智能·ai芯片·ai助手
searchforAI7 小时前
视频画面里的PPT怎么提取?视频转图文讲义的实操教程
人工智能·学习·ai·aigc·powerpoint·音视频·贴图
Rain5097 小时前
mini-cc:一个轻量级 AI 编程助手的诞生
人工智能·typescript·ai编程
hyunbar7 小时前
Fish Audio(鱼声)+ Python:零门槛用自己声音合成任何文本
人工智能
厚国兄7 小时前
Agent_Skills_万千应用_第03篇_PPT 生成 Skill:从资料到可演示幻灯片
人工智能·powerpoint·agent