选中内容(绿色)时除了会搜索文章名,还会搜索文章内容
点击结果中的文章名进入文章界面后可以按Ctrl+F在页面内搜索
  • 版权:CC BY-SA 4.0
  • 创建:2020-06-13
  • 更新:2023-10-18
常见的颜色表示方法, RGB HSV YUV LAB CMYK


RGB

红绿蓝 三色,也是大家熟悉光学三原色

RGB 使用加色模式,也就是默认是黑色,三原色相加获得白色, 比如下图蓝色+绿色=青色(cyan),得到蓝色=青色-绿色也就是蓝色=青色+绿色的互补色, 绿色的互补色就是图中的M(品红色)magenta(同理蓝色的互补色是Y黄色)(互补色就是两者相加为白色),所以蓝色=青色+品红色

rgb

rgb

上图可以看到灰度图在正方体对角线上,即三个通道(轴)的值相等时,值越大越白

RGB3

一般两种表示方法:

RGB888(24bit)

RGB三个通道,每个通道分别用8bit长度表示,比如(255, 255, 255), 每个通道取值范围为[0, 255],或者0xFFFFFF三个字节表示

RGB565(16bit)

RGB三个通道分别用5bit 6bit 5bit表示,比如(31, 63, 31),但一般不这样表示,使用两个字节表示,比如0xFFFF, 一般在编写程序时在内存中多使用以下两种布局方式:
第一种:
rgb565_0
第二种:
rgb5651

这两种方式的不同主要是因为 RGB565 共占用 2 个字节, 两个字节的顺序不同造成的,把第二张图从中间分隔成两份,右边移到左边,就变成了第一种的排列方式了

C语言结构体如下:

  1. #define COLOR_16_SWAP 1
  2. typedef union
  3. {
  4. struct
  5. {
  6. #if COLOR_16_SWAP == 0
  7. uint16_t blue : 5;
  8. uint16_t green : 6;
  9. uint16_t red : 5;
  10. #else
  11. uint16_t green_h : 3;
  12. uint16_t red : 5;
  13. uint16_t blue : 5;
  14. uint16_t green_l : 3;
  15. #endif
  16. } ch;
  17. uint16_t full;
  18. } color16_t;

比如(1,2,3)(R, G, B)
使用第一种方式二进制值为B00001 000010 00011B0000 1000 0100 0011, 十六进制表示为0x0843(注意这里表示方法从左到右是从高位到低位,上面的图从左到右是低位到高位);
使用第二种方式二进制值为B010 00011 00001 000B0100 0011 0000 1000, 十六进制表示为0x4308

HSV

  • Hue(色调、色相)
  • Saturation(饱和度、色彩纯净度)
  • Value(明度)

HSV

HLS

HLS 中的 L 分量为亮度,亮度为100,表示白色,亮度为0,表示黑色;HSV 中的 V 分量为明度,明度为100,表示光谱色,明度为0,表示黑色。

提取白色物体时,使用 HLS 更方便,因为 HSV 中的Hue里没有白色,白色需要由S和V共同决定(S=0, V=100)。而在 HLS 中,白色仅由亮度L一个分量决定。所以检测白色时使用 HSL 颜色空间更准确。

HLS

YUV

Y’UV、YUV、YCbCr、YPbPr 几个概念其实是一回事儿。Y’UV、YUV 主要是用在彩色电视中,用于模拟信号表示。YCbCr 是用在数字视频、图像的压缩和传输,如 MPEG、JPEG。今天大家所讲的 YUV 其实就是指 YCbCr。Y 表示亮度(luma),CbCr 表示色度(chroma)。

另外Y’UV在取值上可以使正或者负数,但是Y’CbCr一般是16–235或者0–255

Y’UV 设计的初衷是为了使彩色电视能够兼容黑白电视。对于黑白电视信号,只需要 Y 通道, 在彩色电视则显示 YUV 信息

yuv
yuv

打包格式和采样

YUV 格式通常有两大类:打包(packed)格式和平面(planar)格式, 前者是每个像素为基本单位,一个一个像素的数据连续排列在内存中,比如 YUYV422(YUV422)(Linux V4L2用到的 V4L2_PIX_FMT_YUYV), 后者则是YUV 分成3个数组(内存块)存放, 另外还有Y和UV分开存放的(比如 YUV420SP( Semi-Planar, U和V交叉放,即YYYYYYYY…UVUV…) 和 YUV420P(先放U再放V,即YYYYYYYY…UUVV))

人眼的视觉特点是对亮度更铭感,对位置、色彩相对来说不铭感。在视频编码系统中为了降低带宽,可以保存更多的亮度信息(luma),保存较少的色差信息(chroma)。这叫做 chrominance subsamping, 色度二次采样。原则:在数字图像中,(1) 每一个图形像素都要包含 luma(亮度)值;(2)几个图形像素共用一个 Cb + Cr 值,一般是 2、4、8 个像素。 一般分为以下几种

  • YUV444:就是每个 Y 值对应一个 U 和 一个 V 值
  • YUV422: 就是图像的横轴(width方向) 4 个 Y 值公用 2 个 U 和V,纵轴(height方向)4 个 Y 对应了 4 个 U 和 4 个 V
  • YUV420: 就是在 YUV422 的基础上, 纵轴的 U 和 V 数量也减半,每 2 个提供给 4 个 Y 配对使用
  • YUV411: 同理,就是 4 个 Y, 对应横轴和纵轴的 1 个 U 和 V

内存中的数据映射到图像像素中就类似下图:

YUV444_YUV422_YUV420

SP(Semi-Planar) 和 P的说法, 区别就是在内存中的存放顺序不同, 比如

YUV420SP:
yuv420p

YUV420P:
yuv420p

另外,还有 NV12 和 NV21 的区别, 就是在 UV 的存放上顺序不同

  • NV12: IOS只有这一种模式。存储顺序是先存Y,再UV交替存储。YYYYUVUVUV,其实就是 YUV420SP 排列
  • NV21: 安卓的模式。存储顺序是先存Y,再存U,再VU交替存储。YYYYVUVUVU, 也是 YUV420SP,只是 V和U交换了顺序

还想了解更多可以自行搜索,比如 详解 YUV 格式(I420/YUV420/NV12/NV12/YUV422)

YUV与RGB之间的转换

YUV 转 RGB 时注意,不同色彩空间的转换公式不相同,比如常见的 BT.601, BT.709, BT.2020 等;
另外, YUV 又分 full range 和 Limited range/TV range, Limited range 的 YUV 取值范围不是 0~255, 而是部分:Y’ 的值被限制在 [16, 235],Cb 和 Cr 的值被限制在 [16, 240]。

linux 下可以通过v4l2-ctl --device /dev/video0 --all命令看到

  1. Format Video Capture:
  2. Width/Height : 1280/720
  3. Pixel Format : 'YUYV' (YUYV 4:2:2)
  4. Field : None
  5. Bytes per Line : 2560
  6. Size Image : 1843200
  7. Colorspace : sRGB
  8. Transfer Function : Rec. 709
  9. YCbCr/HSV Encoding: ITU-R 601
  10. Quantization : Default (maps to Limited Range)
  11. Flags :

此时通过 v4l2 获取到的yuv数据,打印一下,会发现 y 取值属于[15, 235]范围,用此数值转换为 RGB 后, RGB 取值范围也相同,如果直接送给 RGB取值范围[0,255]的显示器则会黑处泛白,需要映射一下。

在网上找现成的转换公式时,可能会遇到瞎复制的没有验证过的文章(并且我也不保证我写的正确hhh

注意这里数据的来源都是网上文章,如果要求高建议找标准文献(http://www.itu.ch/ , 比如 BT709: https://www.itu.int/rec/R-REC-BT.709-6-201506-I),参考文章:

比如 YUV422(默认就是packed格式,planar会加一个P,semi planar 会加一个 SP):
没有深入研究标准文献,不保证正确

  1. static void yuyv422_to_bgr888(void *src, void *dst, int width, int height)
  2. {
  3. uint8_t *rgba = (uint8_t *)dst;
  4. uint8_t *yuyv = (uint8_t *)src;
  5. uint8_t y0, u, y1, v;
  6. float r, g, b;
  7. for (int y = 0; y < height; ++y)
  8. {
  9. for (int x = 0; x < width;)
  10. {
  11. y0 = yuyv[0];
  12. u = yuyv[1];
  13. y1 = yuyv[2];
  14. v = yuyv[3];
  15. // BT601 YUV data is limited range, to RGB[0, 255]
  16. // r = 1.164384 * (y0 - 16) + 1.596 * (v - 128);
  17. // g = 1.164384 * (y0 - 16) - 0.813 * (v - 128) - 0.391 * (u - 128);
  18. // b = 1.164384 * (y0 - 16) + 2.018 * (u - 128);
  19. // BT601 YUV data is full range, to RGB[0, 255]
  20. r = y0 + 1.402 * (v - 128);
  21. g = y0 - 0.344 * (u - 128) - 0.714 * (v - 128);
  22. b = y0 + 1.772 * (u - 128);
  23. // BT709 YUV data is limited range, to RGB[0, 255]
  24. // r = 1.164384 * (y0 - 16) + 1.79271 * (v - 128);
  25. // g = 1.164384 * (y0 - 16) - 0.532909 * (v - 128) - 0.213249 * (u - 128);
  26. // b = 1.164384 * (y0 - 16) + 2.112402 * (u - 128);
  27. // BT709 YUV data is full range, to RGB[0, 255]
  28. // r = y0 + 1.5748 * (v - 128);
  29. // g = y0 - 0.1873 * (u - 128) - 0.4681 * (v - 128);
  30. // b = y0 + 1.8556 * (u - 128);
  31. // BT2020 YUV data is limited range, to RGB[0, 255]
  32. // r = 1.164384 * (y0 - 16) + 1.67867 * (v - 128);
  33. // g = 1.164384 * (y0 - 16) - 0.65042 * (v - 128) - 0.187326 * (u - 128);
  34. // b = 1.164384 * (y0 - 16) + 2.14177 * (u - 128);
  35. // BT2020 YUV data is full range, to RGB[0, 255]
  36. // r = y0 + 1.4746 * (v - 128)
  37. // g = y0 - 0.164553 * (u - 128) - 0.571353 * (u - 128)
  38. // b = y0 + 1.8814 * (u - 128)
  39. r = r > 255 ? 255 : (r < 0 ? 0 : r);
  40. g = g > 255 ? 255 : (g < 0 ? 0 : g);
  41. b = b > 255 ? 255 : (b < 0 ? 0 : b);
  42. rgba[0] = (uint8_t)b;
  43. rgba[1] = (uint8_t)g;
  44. rgba[2] = (uint8_t)r;
  45. // BT601 YUV data is limited range, to RGB[0, 255]
  46. // r = 1.164384 * (y1 - 16) + 1.596 * (v - 128);
  47. // g = 1.164384 * (y1 - 16) - 0.813 * (v - 128) - 0.391 * (u - 128);
  48. // b = 1.164384 * (y1 - 16) + 2.018 * (u - 128);
  49. // BT601 YUV data is full range, to RGB[0, 255]
  50. r = y1 + 1.402 * (v - 128);
  51. g = y1 - 0.344 * (u - 128) - 0.714 * (v - 128);
  52. b = y1 + 1.772 * (u - 128);
  53. // BT709 YUV data is limited range, to RGB[0, 255]
  54. // r = 1.164384 * (y1 - 16) + 1.79271 * (v - 128);
  55. // g = 1.164384 * (y1 - 16) - 0.532909 * (v - 128) - 0.213249 * (u - 128);
  56. // b = 1.164384 * (y1 - 16) + 2.112402 * (u - 128);
  57. // BT709 YUV data is full range, to RGB[0, 255]
  58. // r = y1 + 1.5748 * (v - 128);
  59. // g = y1 - 0.1873 * (u - 128) - 0.4681 * (v - 128);
  60. // b = y1 + 1.8556 * (u - 128);
  61. // BT2020 YUV data is limited range, to RGB[0, 255]
  62. // r = 1.164384 * (y1 - 16) + 1.67867 * (v - 128);
  63. // g = 1.164384 * (y1 - 16) - 0.65042 * (v - 128) - 0.187326 * (u - 128);
  64. // b = 1.164384 * (y1 - 16) + 2.14177 * (u - 128);
  65. // BT2020 YUV data is full range, to RGB[0, 255]
  66. // r = y1 + 1.4746 * (v - 128)
  67. // g = y1 - 0.164553 * (u - 128) - 0.571353 * (u - 128)
  68. // b = y1 + 1.8814 * (u - 128)
  69. r = r > 255 ? 255 : (r < 0 ? 0 : r);
  70. g = g > 255 ? 255 : (g < 0 ? 0 : g);
  71. b = b > 255 ? 255 : (b < 0 ? 0 : b);
  72. rgba[4] = (uint8_t)b;
  73. rgba[5] = (uint8_t)g;
  74. rgba[6] = (uint8_t)r;
  75. yuyv += 4;
  76. rgba += 6;
  77. x += 2;
  78. }
  79. }
  80. }

参考代码:

LAB

Lab颜色空间中的L分量用于表示像素的亮度,取值范围是[0,100],表示从纯黑到纯白;a表示从红色到绿色的范围,取值范围是[127,-128];b表示从黄色到蓝色的范围,取值范围是[127,-128]

LAB

CMY & CMYK

cmyk

一般用在印刷, 因为人眼看到的物体的颜色是反射光,而不是自发光,一束白光照到物体上,默认反射所有,即白色,人眼实际看到的颜色是用光的颜色减去材料吸收后的颜色,这时涂上黑色的颜料,即吸收了所有白光,所以人眼看到的是黑色。
利用色料的三原色混色原理,加上黑色油墨,共计四种颜色混合叠加,形成所谓“全彩印刷”。四种标准颜色是:C:Cyan = 青色,又称为‘天蓝色’或是‘湛蓝’M:Magenta = 品红色,又称为‘洋红色’;Y:Yellow = 黄色K:blacK=黑色,虽然有文献解释说这里的K应该是Key Color(定位套版色),但其实是和制版时所用的定位套版观念混淆而有此一说。此处缩写使用最后一个字母K而非开头的B,是为了避免与Blue混淆。CMYK模式是减色模式,相对应的RGB模式是加色模式。

印刷三原色如何得到黑色, 理论配色如下:

  1. C(100) +M100 +Y100 = 黑色(100100100

可见黑色就是青色、品与黄色之和,但是这三种颜色混成的黑色不够纯,所以印刷学就引进了K(Black)黑色,因为B已经被Blue占用,所以黑色就只好用引文字母黑色的最后一个字母K, 哪么真正印刷的黑色配色如下:

  1. C(100) +M100 +Y100 + K(100) = 黑色 100100100100

或者

  1. C(0) +M(0) + Y(0) + K(100) = 黑色(000100

减色模式:前面说的由于物体是吸收了部分光,才呈现出特有的颜色,比如品红的物体,是吸收了它的互补色绿色(看前面的RGB对互补色的描述),也就是白色光减去了绿色才得到的颜色,所以称之为减色模式

参考

文章有误?有想法想讨论?查看或者发起勘误/讨论 主题
(发起评论需要先登录 github)

/wallpaper/wallhaven-j5lk95.jpg