OpenCV 笔记(21):图像色彩空间

1,973 阅读11分钟

1. 图像色彩空间

图像色彩空间是用于定义颜色范围的数学模型。

它规定了图像中可以使用的颜色以及它们之间的关系。它决定了图像中可以显示的颜色范围。不同的色彩空间可以包含不同的颜色范围,因此选择合适的色彩空间对于确保图像在不同设备上看起来一致非常重要。

图像色彩空间的意义主要体现在以下几个方面:

  • 统一颜色标准:色彩空间提供了一种统一的颜色标准,使得不同设备之间能够相互理解和交流颜色信息。
  • 确保颜色一致性:色彩空间可以确保图像在不同设备上显示时颜色一致。
  • 提高图像质量:色彩空间可以提高图像质量。
  • 简化图像处理:色彩空间可以简化图像处理过程,提高图像处理效率。
  • 促进色彩管理:色彩空间是色彩管理的基础。色彩管理是指确保图像在不同设备上显示时颜色一致的过程。通过使用色彩空间,我们可以将图像转换为与目标设备的色彩空间相匹配的色彩空间,从而确保图像在不同设备上显示时颜色一致。

2. 最常见的图像色彩空间

2.1 RGB

RGB 色彩空间是一种基于红、绿、蓝三原色的加色色彩空间。它是用于显示器和电视机的颜色空间,也是用于大多数数字图像的色彩空间。

RGB 色彩空间使用三个分量来表示颜色:红色、绿色和蓝色。每个分量都是一个介于 0 到 255 之间的数字,其中 0 表示该原色不存在,255 表示该原色完全存在。通过混合不同强度的红、绿、蓝光,可以创建各种颜色。例如,混合相等强度的红、绿和蓝光会产生白色光。混合全强度红色和全强度绿色会产生黄色光。

RGB图1.png

RGB图2.png

RGB 色彩空间根据每个分量在计算机中占用的存储字节数的几种常见情况:

  • RGB24: 每个分量占用8位,共24位,是最常见的RGB格式。
  • RGB565: R分量占用5位,G分量占用6位,B分量占用5位,共16位。这种格式可以节省存储空间,但颜色精度较低。
  • RGB555: R分量、G分量和B分量各占用5位,共15位。这种格式与RGB565类似,但颜色精度更低。
  • RGB32: 每个分量占用8位,共32位。增加了透明度通道,可以用于表示透明图像。这种格式可以提供更高的颜色精度,但需要更多的存储空间。

2.2 HSV

HSV 色彩空间是一种使用色相、饱和度和值来表示颜色的色彩空间。它是一种直观的色彩空间,与人眼感知颜色的方式更加接近。

HSV 色彩空间的三个分量:

  • 色相 (Hue):指的是颜色的基本属性,例如红色、黄色、绿色等。色相通常用角度来表示,范围为0-360度。
  • 饱和度 (Saturation):指的是颜色的纯度,也就是颜色中灰色成分的多少。饱和度越高,颜色越纯净;饱和度越低,颜色越灰暗。饱和度通常用百分比来表示,范围为0-100%。
  • (Value):指的是颜色的亮度,也就是颜色的明暗程度。值越高,颜色越明亮;值越低,颜色越暗淡。值通常用百分比来表示,范围为0-100%。

HSV 是一种将 RGB 色彩空间中的点在倒圆锥体中的表示方法。360度的圆作为色相,饱和度的值就是色相切点指向圆心的线,此时的颜色还是不够用于表示。再引入一个亮度,圆锥的垂线,越往上图片越亮,反之越暗。

HSV图1.png

HSV图2.png

HSV 色彩空间主要应用在以下几个方面:

  • 图像分割:可以根据颜色范围来提取图像中的特定对象。
  • 颜色校正:可以调整图像的饱和度或明度来改善图像质量。
  • 特效处理:可以改变图像的颜色或创建特殊效果。
  • 计算机图形: 可以用于创建逼真的物体和场景。

HSV 颜色对应 RGB 分量范围(通过实验计算的模糊范围)

hsv 对应的 rgb 分量范围.png

2.3 HLS

HLS 色彩空间是一种使用色相、饱和度和明度来表示颜色的色彩空间。

HLS 色彩空间的三个分量:

  • 色相 (Hue): 色相是指颜色的基本属性,通常用角度来表示,范围为0°到360°。0°代表红色,120°代表绿色,240°代表蓝色,360°又回到红色。
  • 饱和度 (Saturation): 饱和度是指颜色的纯度,取值范围为0%到100%。0%表示灰色,100%表示完全饱和。
  • 明度 (Lightness): 明度是指颜色的亮度,取值范围为0%到100%。0%表示黑色,100%表示白色。

HLS.png

下面整理了 HLS 和 HSV 色彩空间的区别:

特性HLSHSV
色相相同相同
饱和度颜色与同亮度、无彩色(灰色)的最大差异程度颜色纯度
亮度感知到的明暗程度颜色的明度
几何形状双圆锥体和圆球体倒圆锥体
适用范围图形设计、用户界面图像处理、计算机图形

HLS和HSV对比.png

2.4 YUV

YUV 色彩空间是一种使用亮度(Y)和两个色度分量(U和V)来表示颜色的颜色空间。它主要用于电视和视频领域。

YUV 色彩空间的三个分量:

  • Y:代表亮度,也就是灰度值。
  • U:代表蓝色和黄色之间的色度分量。
  • V:代表红色和青色之间的色度分量。

YUV.png

由上图可知,亮度和颜色无关只是黑白灰的程度不同,因此 UV 信息代表颜色。

YUV 色彩空间的常见格式:

  • YUV420:一种常见的 YUV 格式,每个像素占用 1.5 个字节。其中 Y 分量占用 1 个字节,U 和 V 分量各占用 0.25 个字节。
  • YUV422:另一种常见的 YUV 格式,每个像素占用 2 个字节。其中 Y 分量占用 1 个字节,U 和 V 分量各占用 0.5 个字节。
  • YUV444: 每个像素占用 3 个字节,其中 Y、U 和 V 分量各占用 1 个字节。这种格式提供最高的图像质量,但需要最多的存储空间。

YUV 色彩空间常见格式:

格式YUV存储空间图像质量
YUV4448位8位8位24位最高
YUV4228位8位 (每隔一个像素采样)8位 (每隔一个像素采样)16位中等
YUV4208位4位 (每隔两个像素采样)4位 (每隔两个像素采样)12位较低

另外,还有 YCbCr,YCbCr 是从 YUV 色彩空间派生而来,但 YCbCr 采用了更复杂的数学公式,以便更好地利用人眼的视觉特性。

2.5 CMY & CMYK

CMY 色彩空间是一种基于减色原理的颜色空间,使用青色 (Cyan)、品红 (Magenta) 和黄色 (Yellow) 三原色来表示颜色。它通常用于印刷和出版领域,因为CMY油墨可以很好地吸收光线,从而产生各种各样的颜色。

CMY.png

CMY 色彩空间的工作原理:白色光包含所有可见光谱。当白色光穿过 CMY 油墨时,油墨会吸收部分光谱。未被吸收的光线会反射到我们的眼睛中,我们看到的就是 CMY 颜色。

CMYK 是 CMY 的扩展增加了黑色分量。这是因为 CMY 无法完美地再现黑色,因此添加黑色分量可以提高阴影和暗调的准确性。

CMY 和 CMYK 色彩空间的比较:

特性CMYCMYK
颜色分量青色、品红、黄色青色、品红、黄色、黑色
颜色模式减色减色
适用范围印刷印刷
黑色分量
色彩范围较小较大
印刷成本较低较高

2.6 Lab

Lab 色彩空间 是一种基于人眼感知的色彩空间,比其他颜色空间(如 RGB 和 CMYK)更接近于感知颜色。

Lab 色彩空间的三个分量:

  • L:代表亮度,取值范围为 0-100,0 表示黑色,100 表示白色。
  • a:代表从红色到绿色的范围,取值范围为 -128 到 127,-128 表示绿色,127 表示红色。
  • b:代表从蓝色到黄色的范围,取值范围为 -128 到 127,-128 表示蓝色,127 表示黄色。

Lab 色彩空间的特性:

  • 感知均匀性好:Lab 色彩空间与人眼感知颜色方式更接近,因此具有较好的感知均匀性。
  • 颜色范围大:Lab 色彩空间可以表示比 RGB 和 CMYK 更广泛的颜色范围。
  • 与设备无关:Lab 色彩空间与设备无关,因此可以在不同的设备之间准确地转换颜色。

Lab.png

3. 图像色彩空间转换

图像色彩空间转换是指将图像从一个色彩空间转换到另一个色彩空间的过程。

OpenCV 提供了 cv::cvtColor() 函数进行图像色彩空间转换,常见的转换方法:

  • RGB 到 HSV:
cv::cvtColor(src, dst, cv::COLOR_RGB2HSV);
  • HSV 到 RGB:
cv::cvtColor(src, dst, cv::COLOR_HSV2RGB);
  • RGB 到 YUV:
cv::cvtColor(src, dst, cv::COLOR_RGB2YUV);
  • YUV 到 RGB:
cv::cvtColor(src, dst, cv::COLOR_YUV2RGB);
  • RGB 到 Lab:
cv::cvtColor(src, dst, cv::COLOR_RGB2Lab);
  • Lab 到 RGB:
cv::cvtColor(src, dst, cv::COLOR_Lab2RGB);

下面的例子,展示了在 OpenCV 中使用 cvtColor()函数将原图转换到各种色彩空间。

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"

using namespace std;
using namespace cv;

int main(int argc, char **argv) {
    Mat src = imread(".../flower.jpg");
    imshow("src", src);

    Mat gray;
    cvtColor(src, gray, cv::COLOR_BGR2GRAY);
    imshow("gray", gray);

    Mat hsv;
    cvtColor(src, hsv, cv::COLOR_BGR2HSV); // BGR 转换到 HSV 色彩空间
    imshow("hsv", hsv);

    Mat hls;
    cvtColor(src, hls, cv::COLOR_BGR2HLS); // BGR 转换到 HLS 色彩空间
    imshow("hls", hls);

    Mat yuv;
    cvtColor(src, yuv, cv::COLOR_RGB2YUV); // BGR 转换到 YUV 色彩空间
    imshow("yuv", yuv);

    Mat lab;
    cvtColor(src, lab, cv::COLOR_RGB2Lab); // BGR 转换到 Lab 色彩空间
    imshow("lab", lab);

    waitKey(0);
    return 0;
}

原图和灰度图.png

hsv和hls色彩空间.png

yuv和lab色彩空间.png

4. 颜色分割

下面的例子,先将图片转换到 HSV 色彩空间,再通过 cv::inRange() 函数将女孩从绿色背景分离出来,最后用蓝色背景替换原先的绿色背景。

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"

using namespace std;
using namespace cv;

int main(int argc, char **argv) {
    Mat src = imread(".../girl.jpg");
    imshow("src", src);

    Mat hsv;
    cvtColor(src, hsv, cv::COLOR_BGR2HSV); // BGR 转换到 HSV 色彩空间
    imshow("hsv", hsv);

    cv::Scalar lower_green(35, 43, 46);
    cv::Scalar upper_green(77, 255, 255); // 定义绿色的 HSV 范围

    Mat mask;
    inRange(hsv, lower_green, upper_green, mask); // 通过 inRange 函数实现二值化
    imshow("mask", mask);

    Mat kernel = getStructuringElement(MORPH_RECT,Size(7,7),Point(-1,-1));
    morphologyEx(mask, mask, MORPH_OPEN, kernel, Point(-1, -1), 1);
    imshow("open", mask);
    GaussianBlur(mask,mask,Size(45,45),0,0);
    imshow("blur", mask);

    bitwise_not(mask,mask);
    imshow("fg", mask);

    // 重新定义背景颜色
    Vec3b color;
    color[0] = 255;
    color[1] = 0;
    color[2] = 0;

    int height = src.rows;
    int width = src.cols;
    Mat result(src.size(),src.type());

    double w = 0.0;
    int b = 0,g = 0, r = 0;
    int b1 = 0,g1 = 0, r1 = 0;
    int b2 = 0,g2 = 0, r2 = 0;

    for (int row = 0; row < height; row++) {
        for (int col = 0; col < width; col++) {
            int m = mask.at<uchar>(row,col);
            if(m == 255){ //前景
                result.at<Vec3b>(row,col) = src.at<Vec3b>(row,col);
            } else if(m==0){ //背景
                result.at<Vec3b>(row,col) = color;
            } else{
                w = m/ 255;
                b1 = src.at<Vec3b>(row,col)[0];//前景
                g1 = src.at<Vec3b>(row,col)[1];
                r1 = src.at<Vec3b>(row,col)[2];

                b2 = color[0];//背景
                g2 = color[1];
                r2 = color[2];

                b = b1*w+b2*(1.0-w);
                g = g1*w+g2*(1.0-w);
                r = r1*w+r2*(1.0-w);

                result.at<Vec3b>(row,col)[0] = b;
                result.at<Vec3b>(row,col)[1] = g;
                result.at<Vec3b>(row,col)[2] = r;
            }
        }
    }

    imshow("result",result);

    waitKey(0);
    return 0;
}

原图以及转换到hsv.png

生成mask和形态学开操作.png

高斯模糊和bitwise_not操作.png

替换背景.png

下面简单介绍一下 cv::inRange() 函数:

void inRange(InputArray src, InputArray lowerb,
                          InputArray upperb, OutputArray dst);

第一个参数 src: 输入图像,可以是单通道或多通道图像。

第二个参数 lowerb: 颜色范围的下限,可以是标量或数组。

第三个参数 upperb: 颜色范围的上限,可以是标量或数组。

第四个参数 dst: 输出图像,与输入图像大小相同,类型为 CV_8U。

对于图像中的每个像素,cv::inRange() 函数会将其与 lowerb 和 upperb 进行比较。如果像素值在两者之间,则输出图像中的对应像素设置为 255,否则设置为 0。所以,实现了图像的二值化。

5. 总结

图像色彩空间在图像处理和显示中起着重要的作用。

除了本文介绍的图像色彩空间之外,还有许多其他图像色彩空间,每种色彩空间都有自己的优势和劣势。

图像色彩空间可以确保颜色一致性、提高图像质量、简化图像处理并促进色彩管理。随着技术的不断发展,图像色彩空间也在不断发展。