• OpenCV3-颜色模型与转换-通道分离与合并



    1.颜色模型介绍

    RGB颜色模型

    RGB图像:通过红、绿、蓝3中颜色不同比例的混合而成,图像以多通道的形式分别存储某一种颜色的红色分量、绿色分量和蓝色分量。

    OpenCV中与RGB的顺序是相反的:第一个通道是蓝色分量,第二个通道是绿色分量,第三个通道是红色分量。3个通道对于颜色描述的范围是相同的,因此,RGB颜色模型的空间构成是一个立方体。

    如果3种颜色分量都为0,表示黑色。
    如果3种颜色分量都为255,表示白色。

    在RGB模型的基础上增加第4个通道即为RGBA模型,第四个通道表示透明度,当没有透明度需求的时候,RGBA模型就会退化成RGB模型。

    YUV颜色模型

    YUV颜色模型是电视信号系统所采用的颜色编码方式。这3个变量分别表示像素的亮度(Y)、红色分量与亮度的信号差值(U)、蓝色与亮度的差值(V)。这种模型主要用于视频和图像传输。

    YUV颜色模型由亮度(Luma)和色度(Chroma)两个分量组成的。

    YUV模型最初是为了在黑白电视上向后兼容彩色信号而引入的。它将彩色信息与亮度分离开来,这样黑白电视就可以只接收亮度信号,而忽略色度信号。随着时间的推移,YUV模型也被广泛应用于数字图像和视频处理领域。

    YUV模型中的亮度分量(Y)表示图像的明暗程度,它对应于灰阶图像。色度分量(U和V)表示颜色信息,它们描述了像素与红色和蓝色之间的差异以及像素与绿色之间的差异。

    在YUV模型中,颜色分量的取值范围通常是0到255(8位表示)。以下是YUV模型中各个分量的含义:

    • Y(亮度):表示像素的明亮程度,取值范围是0到255,其中0代表黑色,255代表白色。
    • U(色度蓝):表示像素与蓝色之间的差异,取值范围是-128到+127,其中-128代表最大蓝色饱和度,+127代表最大红色饱和度。
    • V(色度红):表示像素与红色之间的差异,取值范围是-128到+127,其中-128代表最大绿色饱和度,+127代表最大红色饱和度。

    YUV模型在视频编码、图像处理和电视广播等领域中具有广泛的应用。它能够提供更高的压缩效率,同时在保持图像质量的前提下减小数据量。此外,YUV模型还对人眼感知的特性进行了优化,使得在图像和视频处理中更加符合人眼对亮度和色度的感知特点。

    YUV模型与RGB模型之间可以通过一组转换公式进行相互转换。下面是YUV到RGB的转换公式:

    R = Y + 1.402V
    G = Y - 0.344U - 0.714V
    B = Y + 1.772U
    
    • 1
    • 2
    • 3

    其中,R、G、B是RGB模型中的颜色分量,Y、U、V是YUV模型中的颜色分量。

    而RGB到YUV的转换公式如下:

    Y = 0.299R + 0.587G + 0.114B
    U = -0.147R - 0.289G + 0.436B
    V = 0.615R - 0.515G - 0.100B
    
    • 1
    • 2
    • 3

    需要注意的是,这些转换公式假设RGB的取值范围是0到255,而YUV的取值范围是0到255或-128到+127(8位表示)。如果使用其他范围的值,需要进行归一化或反归一化操作。

    在实际应用中,转换过程可能还涉及到舍入、截断或取整等操作,以适应不同的数据表示格式和精度要求。

    通过这些转换公式,可以在YUV和RGB之间进行颜色空间的转换,从而实现在不同颜色模型下的图像处理和显示。

    HSV颜色模型

    HSV模型将颜色的属性分为三个主要部分:

    1. 色调(Hue):表示颜色的类型或种类。它以角度表示,取值范围为0到360度,对应于色相环。在色相环上,红色位于0度,绿色位于120度,蓝色位于240度。色调决定了颜色在色谱中的位置,例如红色、绿色、蓝色等。
    2. 饱和度(Saturation):表示颜色的纯度或鲜艳程度。它指定了颜色与灰色的相对比例。饱和度的取值范围为0到1,其中0表示灰色,1表示最鲜艳的纯色。较高的饱和度表示颜色更鲜艳,较低的饱和度则使颜色趋向于灰色。
    3. 明度(Value):表示颜色的亮度或明暗程度。它指定了颜色的亮度级别。明度的取值范围也是0到1,其中0表示黑色,1表示最亮的颜色。较高的明度使颜色更亮,较低的明度使颜色变暗。

    HSV模型的优点之一是它更符合人类对颜色的感知。通过调整色调、饱和度和明度,可以实现对颜色的直观控制。HSV模型在图像处理、计算机视觉和图形设计等领域中广泛应用,特别是在颜色选择、颜色调整和颜色分析方面。

    HSV模型与RGB模型之间可以相互转换。转换公式如下:

    RGB到HSV的转换:

    • 计算最大值(max)和最小值(min):max = max(R, G, B),min = min(R, G, B)。
    • 计算色调(Hue):
      • 如果max = min,则Hue = 0(色相无定义)。
      • 如果max = R,则Hue = (G - B) / (max - min)。
      • 如果max = G,则Hue = 2 + (B - R) / (max - min)。
      • 如果max = B,则Hue = 4 + (R - G) / (max - min)。
      • 将Hue乘以60度,并将负值加上360度,以确保结果在0到360度之间。
    • 计算饱和度(Saturation):
      • 如果max = 0,则Saturation = 0(饱和度为0)。
      • 否则,Saturation = (max - min) / max。
    • 计算明度(Value):Value = max。

    HSV到RGB的转换:

    • 如果Saturation = 0,则RGB的三个分量均为Value。
    • 否则,计算色相对应的六个部分(区间):H/60。
    • 将Value乘以饱和度(Saturation)和1减去饱和度的乘积(1 - Saturation * f)并得到中间值(p)。
    • 将Value减去饱和度乘以Value的乘积(q)并得到中间值(t)。
    • 根据色相所在的区间,设置RGB分量的值:
      • 区间0:(Value, t, p)
      • 区间1:(q, Value, p)
      • 区间2:(p, Value, t)
      • 区间3:(p, q, Value)
      • 区间4:(t, p, Value)
      • 区间5:(Value, p, q)

    以上是HSV模型和RGB模型之间的相互转换请注意,上述转换公式中的RGB值范围假设为0到1。如果RGB值范围是0到255(8位表示),需要进行归一化或反归一化操作,将RGB值除以255或乘以255来适应不同的数据表示格式。

    Lab颜色模型

    Lab颜色模型,也称为CIELAB颜色空间,是一种用于描述人类视觉感知的颜色模型。它是由国际照明委员会(CIE)于1976年提出的,旨在提供一种与人眼感知相关的均匀颜色空间。

    Lab模型将颜色分解为三个分量:

    1. 亮度(L):表示颜色的明暗程度,取值范围为0到100,其中0表示黑色,100表示白色。
    2. 色度(a):表示颜色在红绿轴上的位置,取值范围为-128到+127。负值表示绿色,正值表示红色。
    3. 色度(b):表示颜色在黄蓝轴上的位置,取值范围为-128到+127。负值表示蓝色,正值表示黄色。

    Lab模型的优点之一是它与人类的视觉感知更加一致。在Lab空间中,相同距离的颜色变化在人眼中看起来是均匀的。这使得Lab模型在颜色比较、颜色差异计算和颜色校正等方面具有广泛的应用。

    Lab模型与RGB模型之间可以相互转换。转换过程需要通过一组数学公式进行计算。具体的转换公式比较复杂,涉及到非线性的数学操作。

    Lab到RGB的转换涉及到逆变换,而RGB到Lab的转换则是正变换。这些转换公式包含了颜色空间的非线性特性,以及对RGB颜色空间的边界处理。

    由于转换公式较为复杂,通常需要使用计算机或专门的图像处理软件来进行Lab和RGB之间的颜色转换。这些软件提供了方便的函数或工具,可以直接进行Lab和RGB颜色空间之间的转换操作。

    总结来说,Lab颜色模型是一种与人眼感知相关的颜色模型,它提供了一种均匀的颜色空间,与RGB模型之间可以相互转换,用于颜色比较、差异计算和校正等应用。

    GRAY颜色模型

    GRAY颜色模型(或灰度颜色模型)也被称为黑白或单色模型。与RGB模型不同,GRAY模型仅使用单个通道来表示图像的亮度信息,而不考虑颜色。

    在GRAY模型中,每个像素的灰度值表示了该像素的亮度级别。灰度值的范围通常是从0到255,其中0表示黑色,255表示白色,中间的值表示不同亮度级别的灰色。

    GRAY模型的优点之一是它具有简单性和直观性。由于只有一个通道,处理灰度图像相对较为简单,并且不需要考虑颜色信息。因此,GRAY模型常用于各种应用,例如图像处理、计算机视觉、模式识别等领域。

    在图像处理中,可以通过将彩色图像转换为灰度图像来简化处理过程。常见的灰度转换方法包括将RGB图像的三个通道的值进行平均,或者使用带有不同权重的通道值的线性组合。

    在许多图像处理软件和编程库中,都提供了方便的函数和工具来进行RGB到GRAY的转换,以及在GRAY图像上进行各种处理和分析操作。

    需要注意的是,GRAY模型只包含亮度信息,而不包含颜色信息。因此,在某些需要考虑颜色的应用中,可能需要使用RGB或其他颜色模型来表示图像的完整颜色信息。

    在RGB模型和GRAY模型之间进行转换涉及将彩色图像转换为灰度图像,或者将灰度图像转换为彩色图像。下面分别介绍两种转换方法:

    1. RGB到GRAY的转换:
      在将RGB图像转换为灰度图像时,可以使用以下常见的方法之一:

      • 平均法:将RGB图像的红色、绿色和蓝色通道的值进行平均,然后将结果作为灰度图像的单个通道值。
      • 加权平均法:不同颜色通道的亮度贡献度可能不同,可以使用一组权重来对红色、绿色和蓝色通道的值进行加权平均,得到灰度图像的单个通道值。常用的加权平均法是将红色通道乘以0.299、绿色通道乘以0.587,蓝色通道乘以0.114,然后将它们相加。
      • 其他方法:除了平均法和加权平均法,还有一些其他转换方法,例如最大值法、最小值法、亮度法等。这些方法根据不同的需求和应用选择。
    2. GRAY到RGB的转换:
      从灰度图像转换回彩色图像比较困难,因为灰度图像丢失了原始彩色图像的颜色信息。在这种情况下,转换是不可逆的,即无法恢复原始的彩色图像。因此,从灰度图像到RGB图像的转换通常是一种伪彩色化过程。

      伪彩色化通常涉及将灰度图像映射到一组颜色映射表(color map)或者应用某种色彩分布算法来生成彩色图像。这样可以根据灰度级别为每个像素赋予对应的颜色值。常见的颜色映射表包括热图(heatmap)、彩虹图(rainbow)、灰度级别映射等。

    需要注意的是,从GRAY到RGB的转换是一种主观的过程,可以根据需求和应用选择不同的伪彩色化方法和颜色映射表。

    总结来说,RGB到GRAY的转换可以使用平均法、加权平均法或其他方法,将RGB图像的颜色信息转换为灰度图像的亮度信息。而从GRAY到RGB的转换是一种伪彩色化过程,通过为灰度级别赋予对应的颜色值,生成彩色图像。

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

    void cvtColor( InputArray src, OutputArray dst, int code, int dstCn = 0 );
    
    • 1

    code参数表示颜色空间转换的标志:如COLOR_BGR2BGRA、COLOR_BGR2GRAY

    dstCn表示目标图像的通道数,0表示由src自动计算出。

    需要注意的是该函数变换前后的图像取值范围,注意目标图像的像素范围。在线性变换下,范围问题不需要考虑,目标图像的像素不会超出范围。如果在非线性变换下,那么应将输入的RGB图像归一化到合适的范围以内来获得正确的结果。例如将8为无符号图像转换成32位浮点图像,需要先将图像像素通过除以255缩放到0-1之间(最值归一化)。

    #include 
    #include 
    
    using namespace cv;
    using namespace std;
    
    Mat add_gaussian_noise(Mat& image, float average = 0.0, float standard_deviation = 10.0)
    {
    	Mat result = image.clone();
    	/* Need to using signed images, as noise can be negative as well as positive.
    	   We use 16 bit signed images as otherwise we would lose precision */
    	Mat noise_image(result.size(), CV_16SC3);
    	randn(noise_image, Scalar::all(average), Scalar::all(standard_deviation));
    	Mat temp_image;
    	result.convertTo(temp_image, CV_16SC3);
    	addWeighted(temp_image, 1.0, noise_image, 1.0, 0.0, temp_image);
    	temp_image.convertTo(result, image.type());
    
    	return result;
    }
    
    int main()
    {
    	cout << "OpenCV Version: " << CV_VERSION << endl;
    	utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);
    
    	Mat img = imread("lena.png");
    
    	string winname = "lena";
    	imshow(winname, img);
    
    	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);
    
    	int k = waitKey(0); // Wait for a keystroke in the window
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    上面代码中使用了Mat类自带的数据类型转换函数convertTo():将已有图像转换为指定数据类型的图像。

    void GpuMat::convertTo(OutputArray dst,  // 转换后输出图像
                           int rtype,        // 转换图像的类型
                           double alpha,     // 缩放因子
                           double beta) const// 偏置因子
    
    • 1
    • 2
    • 3
    • 4

    将原有数据进行先行转换:

    m(x,y) = saturate_cast(alpha(*this)(x,y) + beta);

    3.多通道分离与合并

    void split(const Mat& src,  // 待分离的多通道图像
               Mat* mvbegin);   // 分离后的单通道图像,应传入数组,数组大小需要与图像通道数相同
    
    void split(InputArray m,    // 待分离的多通道图像
               OutputArrayOfArrays mv); // 分离后的单通道图像,vector容器
    
    
    void merge(const Mat* mv,    // 需要合并的图像数组,数组中不一定都要是单通道,但必须拥有相同的尺寸和数据类型 
               size_t count,     // 输入图像的长度
               OutputArray dst); // 合并后输出图像
    
    void merge(InputArrayOfArrays mv, // 需要合并的图像向量
               OutputArray dst);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    下面例子展示了lena.png 3通道图像的分离,以及分离以后将蓝红分量置0合并成仅有绿分量的图像result1。用HSV图像演示向量版本的使用:

    #include 
    #include 
    
    using namespace cv;
    using namespace std;
    
    int main()
    {
    	cout << "OpenCV Version: " << CV_VERSION << endl;
    	utils::logging::setLogLevel(utils::logging::LOG_LEVEL_SILENT);
    
    	Mat img = imread("lena.png");
    
    	Mat result0, result1, result2;  //多通道合并的结果
    
    	//1.输入数组参数的多通道分离与合并
    	Mat imgs[3];
    	split(img, imgs);
    
    	Mat imgs0, imgs1, imgs2;
    	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);  //显示合并结果
    
    	//2.输入vector参数的多通道分离与合并
    	Mat HSV;
    	cvtColor(img, HSV, COLOR_RGB2HSV);
    	vector<Mat> imgv;
    
    	split(HSV, imgv);
    
    	Mat imgv0, imgv1, imgv2;
    	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中查看
    
    	int k = waitKey(0); // Wait for a keystroke in the window
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
  • 相关阅读:
    在C/C++中使用vcpkg
    Gitlab常用命令总结汇总
    【Linux】动态库与静态库的底层比较
    JS----前端不同格式的 UUID生成
    持续集成与持续交付(CI/CD):探讨在云计算中实现快速软件交付的最佳实践
    众和策略:尾盘5分钟拉升意味着什么?
    Java 类集 习题
    Linux 中的 cpio 命令及示例
    es安装方式
    测试开发春招
  • 原文地址:https://blog.csdn.net/ArthurHai521/article/details/133678344