Opencv学习-图像颜色空间

        通过红、绿、蓝 3 种颜色不同比例的混合能够让图像展现出五彩斑斓的颜色,这种模型称为
RGB 颜色模型。 RGB 颜色模型是最常见的颜色模型之一,常用于表示和显示图像。为了能够表 示 3 种颜色的混合,图像以多通道的形式分别存储某一种颜色的红色分量、绿色分量和蓝色分量。
RGB 颜色模型外,图像的颜色模型还有 YUV HSV 等模型,分别表示图像的亮度、色度、饱
和度等分量。了解图像颜色空间对分割拥有颜色区分特征的图像具有重要的帮助,例如提取图像中
的红色物体可以通过比较图像红色通道的像素值来实现。

1.  颜色模型与转换

       OpenCV 4 中能够互相转换的常见颜色模型,例如 RGB 模型、 HSV 模型、Lab 模型、 YUV 模型及 GRAY 模型,并介绍这几种模型之间的数学转换关系、 OpenCV 4 中提供的这几种模型之间的变换函数。

1.1 RGB 颜色模型

        前面对于 RGB 颜色模型已经有所介绍,该模型的命名方式是采用 3 种颜色的英文首字母,分
别是红色( Red )、绿色( Green )和蓝色( Blue )。虽然该颜色模型的命名方式是红色在前,但是在 OpenCV 中却是相反的顺序,第一个通道是蓝色( B )分量,第二个通道是绿色( G )分量,第三个通道是红色(R )分量。根据存储顺序的不同, OpenCV 4 中提供了这种顺序的反序格式,用于 存储第一个通道是红色分量的图像,但是这两种格式图像的颜色空间是相同的,颜色空间模型如下图 所示。 3 个通道对于颜色描述的范围是相同的,因此 RGB 颜色模型的空间构成是一个立方体。在 RGB 颜色模型中,所有的颜色都是由这 3 种颜色通过不同比例的混合得到,如果 3 种颜色分量都为 0 ,则表示为黑色,如果 3 种颜色的分量相同且都为最大值,则表示为白色。每个通道都表示某一种颜色 0 1 的过程,不同位数的图像表示将这个颜色变化过程细分成不同的层级,例如8UC3 格式的图像每个通道将这个过程量化成 256 个等级,分别由 0 255 表示。在这个模型的基础上增加第四个通道即为 RGBA 模型,第四个通道表示颜色的透明度,当没有透明度需求的时候, RGBA 模型就会退化成 RGB 模型。

RGB 颜色空间模型  

1.2 YUV 颜色模型  

        YUV 模型是电视信号系统所采用的颜色编码方式。这 3 个变量分别表示像素的亮度( Y )、红色分量与亮度的信号差值(U )、蓝色与亮度的差值( V )。这种颜色模型主要用于视频和图像的传输,该模型的产生与电视机的发展历程密切相关。由于彩色电视机在黑白电视机发明之后才产生,因此用于彩色电视机的视频信号需要能够兼容黑白电视机。彩色电视机需要 3 个通道的数据才能显示彩色,而黑白电视机只需要一个通道的数据,因此,为了使视频信号能够兼容彩色电视机与黑白电视机,将 RGB 编码方式转变成 YUV 的编码方式,其 Y 通道是图像的亮度,黑白电视只需要使用该通道就可以显示黑白视频图像,而彩色电视机通过将 YUV 编码转成 RGB 编码方式,便可以在彩色电视机中显示彩色图像,较好地解决了同一个视频信号兼容不同类型电视机的问题。RGB模型与 YUV 模型之间的转换关系如图 所示,其中 RGB 取值范围均为 0 255

1.3 HSV 颜色模型  

        HSV 是色度( Hue )、饱和度( Saturation )和亮度( Value )的简写,通过名字也可以看出该模型通过这 3 个特性对颜色进行描述。色度是色彩的基本属性,就是平时常说的颜色,例如红色、蓝色等;饱和度是指颜色的纯度,饱和度越高色彩越纯和越艳,饱和度越低,色彩则逐渐地变灰和变暗,饱和度的取值范围是 0 100% ;亮度是颜色的明亮程度,其取值范围由 0 到计算机中允许的最大值。由于色度、饱和度和亮度的取值范围不同,因此其颜色空间模型用锥形表示,如下图 所示。相比于 RGB 模型 3 个颜色分量与最终颜色联系不直观的缺点, HSV 模型更加符合人类感知颜色的方式:颜色、深浅及亮暗。

HSV 颜色空间模型  

1.4 Lab 颜色模型

        Lab 颜色模型弥补了 RGB 模型的不足,是一种设备无关和基于生理特征的颜色模型。在模型
中, L 表示亮度( Luminosity ), a b 是两个颜色通道,两者的取值区间都是 −128 127 ,其中 a通道数值由小到大对应的颜色是从绿色变成红色,b 通道数值由小到大对应的颜色是由蓝色变成黄色。Lab 颜色模型构成的颜色空间是一个球形。

Lab 颜色空间模型  

1.5 GRAY 颜色模型

        GRAY 模型并不是一个彩色模型,而是一个灰度图像的模型,其命名使用的是英文单词 gray
的全字母大写。灰度图像只有单通道,灰度值根据图像位数不同由 0 到最大依次表示由黑到白,例
8UC1 格式中,由黑到白被量化为 256 个等级,通过 0 255 表示,其中 255 表示白色。彩色图像具有颜色丰富、信息含量大的特性,但是灰度图在图像处理中依然具有一定的优势。例如,灰度图像具有相同尺寸相同压缩格式所占容量小、易于采集、便于传输等优点。常用的 RGB 模型转成 灰度图的方式如下图 所示。

2. 不同颜色模型间的互相转换

针对图像不同颜色模型之间的相互转换, OpenCV 4 提供了 cvtColor() 函数用于实现转换功能。

2.1  cvtColor()函数原型

void cv::cvtColor(InputArray src, 
OutputArray dst, 
int code, 
int dstCn = 0 
)
src :待转换颜色模型的原始图像。
dst :转换颜色模型后的目标图像。
code :颜色模型转换标志,如由 RGB 空间到 HSV 空间
dstCn :目标图像中的通道数。如果参数为 0 ,则从 src 和代码中自动导出通道数。
        函数用于将图像从一个颜色模型转换为另一个颜色模型,前两个参数用于输入待转换图像和转换颜色空间后的目标图像,第三个参数用于声明该函数具体的转换模型空间,常用的标志在下表中给出, 第四个参数在一般情况下不需要特殊设置,使用默认参数即可。需要注意的是该函数变换前后的图像取值范围,由于 8 位无符号图像的像素为 0 255 16 位无符号图像的像素为 0 65 535 ,而 32 位浮点图像的像素为 0 1 ,因此一定要注意目标图像的像素范围。在线性变换的情况下,范围问题不需要考虑,目标图像的像素不会超出范围。如果在非线性变换的情况下,那么应将输入 RGB 图像归一化到适当的范围以内来获得正确的结果,例如将 8 位无符号图像转成 32 位浮点图像,需要先将图像像素通过除以 255 缩放到 0 1 范围内,以防止产生错误结果。
颜色模型转换常用标志

        程序中需要说明的是,Lab 颜色模型具有负数,而通过 imshow()函数显示的图像无法显示负数,因此在结果中给出了利用 Image Watch 插件显示图像在 Lab模型中的样子。在程序中,为了防止转换后出现数值越界的情况,我们先将CV_8U 类型转成 CV_32F类型后再进行颜色模型的转换。

2.2 示例代码

#include <opencv2/opencv.hpp> 
#include <iostream> 
#include <vector> 

using namespace std; 
using namespace cv; 

int main() 
{ 
    Mat img = imread("../pic/gril_1.jpg"); 
    if (img.empty()) 
    { 
        cout << "请确认图像文件名称是否正确" << endl; 
        return -1; 
    } 
    Mat gray, HSV, YUV, Lab, img32; 
    img.convertTo(img32, CV_32F, 1.0 / 255); //将 CV_8U 类型转换成 CV_32F 类型
    //img32.convertTo(img, CV_8U, 255); //将 CV_32F 类型转换成 CV_8U 类型
    cvtColor(img32, HSV, COLOR_BGR2HSV); 
    cvtColor(img32, YUV, COLOR_BGR2YUV); 
    cvtColor(img32, Lab, COLOR_BGR2Lab); 
    cvtColor(img32, gray, COLOR_BGR2GRAY); 
    imshow("原图", img32); 
    imshow("HSV", HSV);
    imshow("YUV", YUV); 
    imshow("Lab", Lab); 
    imshow("gray", gray); 
    waitKey(0); 
    return 0; 
}

2.3 运行结果

2.3 convertTo()函数原型

void cv::Mat::convertTo(OutputArry m, 
int rtype, 
double alpha = 1, 
double beta = 0 
)
m :转换类型后输出的图像。
rtype :转换图像的数据类型。
alpha :转换过程中的缩放因子。
beta :转换过程中的偏置因子。

        该函数用来实现将已有图像转换成指定数据类型的图像,第一个参数用于输出转换数据类型后的图像,第二个参数用于声明转换后图像的数据类型。第三个参数与第四个参数用于声明两个数据类型间的转换关系,具体转换形式如下图所示。

        通过转换公式可以知道,该转换方式就是将原有数据进行线性转换,并按照指定的数据类型输 出。根据其转换规则可以知道,该函数不但能够实现不同数据类型之间的转换,而且能够实现在同 一种数据类型中的线性变换。我们在示例代码 中给出了 CV_8U 类型和 CV_32F 类型之间互相转换的示例。

2.4  saturate_cast函数简单介绍

 saturate_cast<uchar>主要是为了防止颜色溢出操作(0~255)
if(data<0)
        data=0;
else if(data>255)
        data=255;
else
        data不变;
int a ,b ,c, d,f,e;
a = 100;
b = 200; 
c = -200;
d =  saturate_cast<uchar>(a + b);
f =  saturate_cast<uchar>(c);
e =  saturate_cast<uchar>(a);
cout << " d == " << d << "  f == " << f  <<"  e == " << e <<  endl;

 运行结果:

3. 多通道分离与合并  

        在图像颜色模型中,不同的分量存放在不同的通道中,如果我们只需要颜色模型的某一个分量,例如只需要处理 RGB 图像中的红色通道,那么可以将红色通道从 3 个通道的数据中分离出来再进行处理,这种方式可以减少数据所占据的内存,加快程序的运行速度。同时,当我们分别处理完多个通道后,需要将所有通道合并在一起重新生成 RGB 图像。针对图像多通道的分离与混合,
OpenCV 4 中提供了 split() 函数和 merge() 函数用于满足这些需求。

3.1 split()函数原型

void cv::split(const Mat & src, Mat * mvbegin ) 
void cv::split(InputArray m, OutputArrayOfArrays mv )
  • src:待分离的多通道图像。
  • mvbegin:分离后的单通道图像,为数组形式,数组大小需要与图像的通道数相同。
  • m:待分离的多通道图像。
  • mv:分离后的单通道图像,为向量(vector)形式。

        ​​该函数主要是用于将多通道的图像分离成若干单通道的图像,两个函数原型中不同之处在于, 前者第二个参数输出的是 Mat 类型的数组,其数组的长度需要与多通道图像的通道数相等并且提前 定义;第二种函数原型的第二个参数输出的是一个 vector<Mat>容器,不需要知道多通道图像的通道数。

3.2 多通道合并函数 merge()

void merge(const Mat* mv, size_t count, OutputArray dst);
void merge(const vector& mv, OutputArray dst );

mv(第一种重载原型参数):需要合并的图像数组,其中每个图像必须拥有相同的尺寸和数据类型。
count:输入的图像数组的长度,其数值必须大于 0。
mv(第二种重载原型参数):需要合并的图像向量(vector),其中每个图像必须拥有相同的尺寸和数据类型。
dst:合并后输出的图像,与 mv[0]具有相同的尺寸和数据类型,通道数等于所有输入图像的通道数总和。

        该函数主要用于将多个图像合并成一个多通道图像,该函数也具有两种不同的函数原型,每一种函数原型都与 split() 函数相对应,两种原型分别输入数组形式的图像数据和向量( vector )形式的图像数据,在输入数组形式数据的原型中,还需要输入数组的长度。合并函数的输出结果是一个多通道的图像,其通道数目是所有输入图像通道数目的总和。这里需要说明的是,用于合并的图像并非都是单通道的,也可以是多个通道数目不相同的图像合并成一个通道更多的图像。虽然这些图像的通道数目可以不相同,但是需要所有图像具有相同的尺寸和数据类型。

3.3 示例代码

#include <opencv2/opencv.hpp> 
#include <iostream> 
#include <vector> 
 
using namespace std; 
using namespace cv; 
 
int main() 
{ 
    Mat img = imread("../pic/hand.jpg"); 
    if (img.empty()) 
    { 
        cout << "请确认图像文件名称是否正确" << endl; 
        return -1; 
    } 
    Mat HSV; 
    cvtColor(img, HSV, COLOR_RGB2HSV); 
    Mat imgs0, imgs1, imgs2; //用于存放数组类型的结果
    Mat imgv0, imgv1, imgv2; //用于存放 vector 类型的结果
    Mat result0, result1, result2; //多通道合并的结果
    
    //输入数组参数的多通道分离与合并
    Mat imgs[3]; 
    split(img, imgs); 
    imgs0 = imgs[0]; 
    imgs1 = imgs[1]; 
    imgs2 = imgs[2]; 
    // imshow("RGB-B 通道", imgs0); //显示分离后 B 通道的像素值
    // imshow("RGB-G 通道", imgs1); //显示分离后 G 通道的像素值
    // imshow("RGB-R 通道", imgs2); //显示分离后 R 通道的像素值
    //imgs[2] = img; //将数组中的图像通道数变成不一致
    merge(imgs, 3, result0); //合并图像
    imshow("result0", result0); //imshow 最多显示 4 个通道,因此结果在 Image Watch 中查看
    Mat zero = cv::Mat::zeros(img.rows, img.cols, CV_8UC1); 
    imgs[0] = zero; 
    imgs[2] = zero; 
    merge(imgs, 3, result1); //用于还原 G 通道的真实情况,合并结果为绿色
    imshow("result1", result1); //显示合并结果
    
    //输入 vector 参数的多通道分离与合并
    vector<Mat> imgv; 
    split(img, imgv); 
    // imgv0 = imgv.at(0); 
    // imgv1 = imgv.at(1); 
    // imgv2 = imgv.at(2); 
    // imshow("HSV-H 通道", imgv0); //显示分离后 H 通道的像素值
    // imshow("HSV-S 通道", imgv1); //显示分离后 S 通道的像素值
    // imshow("HSV-V 通道", imgv2); //显示分离后 V 通道的像素值
    //imgv.push_back(HSV); //将 vector 中的图像通道数变成不一致
    merge(imgv, result2); //合并图像
    imshow("result2", result2); //imshow 最多显示 4 个通道,因此结果在 Image Watch 中查看
    waitKey(0); 
    return 0; 
}

3.4 运行结果

 

 

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值