• 走进音视频的世界——RGB与YUV格式


    在图像的世界里,一般使用RGB作为存储格式。而在视频的世界里,一般使用YUV作为压缩存储格式。有时候面试官会问:为什么视频使用YUV来压缩存储,而不用RGB?YUV与RGB有什么区别,两者如何转换的?常见的RGB格式有哪些,常见的YUV格式又有哪些?手机摄像头的预览格式是什么,如何转换为YUV420P的?我们带着这些问题,来揭开RGB与YUV格式的面纱。 

     

    目录

    一、RGB格式

    1、RGBA8888

    2、RGB565

    3、图像的像素阵列

    二、YUV格式

    1、YUV420p

    2、YUV420sp

    3、NV21

    三、RGB与YUV转换

    四、NV21转换为YUV420p

    五、YUV旋转


    一、RGB格式

    RGB是一种图像存储格式,也是三原色,取值范围[0, 255]。R代表Red红色,G代表Green绿色,B代表Blue蓝色。在openCV中,一般使用BGR格式。在图像中,一般使用32位色的ARGB(或RGBA)代表一个像素,其中A代表Alpha透明度。常见的RGB格式有RGB888、RGBA8888、RGB565等。

    1、RGBA8888

    关于RGBA8888格式,每个通道占8位,即一个字节。四个通道构成一个像素,总共占32位。排列顺序如下图所示:

    2、RGB565

    关于RGB565格式,其中R占5位,G占6位,B占5位。三个通道构成一个像素,总共占16位。排列顺序如下图所示:

    3、图像的像素阵列

    一张图像由宽x高的像素阵列构成,为了内存对齐,会使用stride来填充。如下图所示,由4x3构成的像素阵列,其中P代表pixel:

    二、YUV格式

    YUV是一种视频压缩存储格式。其中Y代表Luma亮度,U代表Chroma色度,V代表Contrast对比度。常见的YUV采样比例如下:

    • 4:4:4 表示完全采样
    • 4:2:2 表示水平2:1采样,垂直完全采样
    • 4:2:0 表示水平2:1采样,垂直2:1采样
    • 4:1:1 表示水平4:1采样,垂直完全采样

    常见的YUV格式有:YUV420p、YUV420sp、NV21等。由于U和V分量都是Y分量的1/4,而RGB888的所有分量占比都是1。进一步可得,YUV整体占比是3/2,RGB整体占比是6/2,YUV所占存储空间比RGB少了3/2。因此,默认采用YUV作为视频压缩存储格式。

    1、YUV420p

    YUV420p属于平面存储,YUV分量占比为4:1:1,即每4个Y共享一组UV。先是Y分量,然后是U分量,最后是V分量。排列如下图所示:

    2、YUV420sp

    YUV420sp属于交错存储,YUV分量占比为4:1:1,即每4个Y共享一组UV。先是Y分量,然后是UV分量交错存储。排列如下图所示:

    3、NV21

    NV21属于交错存储,YUV分量占比为4:1:1,即每4个Y共享一组UV。Android手机摄像头预览数据默认是NV21格式。和YUV420sp的区别是,NV21是VUVU这样排列,如下图所示:

    三、RGB与YUV转换

    关于YUV与RGB的转换公式,可参考ITU标准:https://www.itu.int/rec/R-REC-BT.601。也可以参考维基百科:https://zh.wikipedia.org/wiki/YUV。咱们来看下转换公式:

    以rgb转yuv为例,示例代码如下:

    1. void rgb_to_yuv(int8_t *yuv, int *rgb, int width, int height) {
    2. int rgbIndex = 0;
    3. int yIndex = 0;
    4. int uIndex = width * height;
    5. int vIndex = width * height * 5 / 4;
    6. int R, G, B;
    7. float Y, U, V;
    8. // 遍历图像,获取所有像素点
    9. for (int i = 0; i < height; i++) {
    10. for (int j = 0; j < width; j++) {
    11. // 从像素点获取R、G、B分量
    12. R = (rgb[rgbIndex] & 0xFF0000) >> 16;
    13. G = (rgb[rgbIndex] & 0xFF00) >> 8;
    14. B = (rgb[rgbIndex] & 0xFF);
    15. // 使用公式把RGB转成YUV
    16. Y = 0.299 * R + 0.587 * G + 0.114 * B;
    17. U = -0.147 * R - 0.289 * G + 0.436 * B;
    18. V = 0.615 * R - 0.515 * G - 0.100 * B;
    19. // YUV分量赋值给yuv数组
    20. yuv[yIndex++] = (int8_t)Y;
    21. if (i % 2 == 0 && j % 2 == 0) {
    22. yuv[uIndex++] = (int8_t) U;
    23. yuv[vIndex++] = (int8_t) V;
    24. }
    25. rgbIndex++;
    26. }
    27. }
    28. }

    四、NV21转换为YUV420p

    由于NV21是交错存储,4个Y共享一组UV,而且是VUVU这样排列。所以,我们需要把偶数的V分量、奇数的U分量读出来,然后赋值给YUV420p。代码如下:

    1. static void nv21_to_yuv420p(int8_t *dst, int8_t *src, int len) {
    2. memcpy(dst, src, len); // y
    3. for (int i = 0; i < len / 4; ++i) {
    4. *(dst + len + i) = *(src + len + i * 2 + 1); // u
    5. *(dst + len * 5 / 4 + i) = *(src + len + i * 2); // v
    6. }
    7. }

    五、YUV旋转

    YUV的存储是有旋转角度的存在。在手机拍摄时,按照逆时针来看,横屏向左是0度,竖屏向下是90度,横屏向右是180度,竖屏向上是270度。既然有旋转角度,我们就需要对YUV进行旋转处理,代码如下:

    1. static void yuv420p_rotate90(int8_t *dst, const int8_t *src, int width, int height) {
    2. int n = 0;
    3. int wh = width * height;
    4. int half_width = width / 2;
    5. int half_height = height / 2;
    6. // y
    7. for (int j = 0; j < width; j++) {
    8. for (int i = height - 1; i >= 0; i--) {
    9. dst[n++] = src[width * i + j];
    10. }
    11. }
    12. // u
    13. for (int i = 0; i < half_width; i++) {
    14. for (int j = 1; j <= half_height; j++) {
    15. dst[n++] = src[wh + ((half_height - j) * half_width + i)];
    16. }
    17. }
    18. // v
    19. for (int i = 0; i < half_width; i++) {
    20. for (int j = 1; j <= half_height; j++) {
    21. dst[n++] = src[wh + wh / 4 + ((half_height - j) * half_width + i)];
    22. }
    23. }
    24. }
    25. static void yuv420p_rotate180(int8_t *dst, const int8_t *src, int width, int height) {
    26. int n = 0;
    27. int half_width = width / 2;
    28. int half_height = height / 2;
    29. // y
    30. for (int j = height - 1; j >= 0; j--) {
    31. for (int i = width; i > 0; i--) {
    32. dst[n++] = src[width * j + i - 1];
    33. }
    34. }
    35. // u
    36. int offset = width * height;
    37. for (int j = half_height - 1; j >= 0; j--) {
    38. for (int i = half_width; i > 0; i--) {
    39. dst[n++] = src[offset + half_width * j + i - 1];
    40. }
    41. }
    42. // v
    43. offset += half_width * half_height;
    44. for (int j = half_height - 1; j >= 0; j--) {
    45. for (int i = half_width; i > 0; i--) {
    46. dst[n++] = src[offset + half_width * j + i - 1];
    47. }
    48. }
    49. }
    50. static void yuv420p_rotate270(int8_t *dst, const int8_t *src, int width, int height) {
    51. for (int j = 0; j < width; j++) {
    52. for (int i = 1; i <= height; i++) {
    53. *dst++ = *(src + i * width - j);
    54. }
    55. }
    56. auto *src_u = const_cast<int8_t *>(src + width * height);
    57. for (int j = 0; j < width / 2; j++) {
    58. for (int i = 1; i <= height / 2; i++) {
    59. *dst++ = *(src_u + i * width / 2 - j);
    60. }
    61. }
    62. auto *src_v = const_cast<int8_t *>(src + width * height * 5 / 4);
    63. for (int j = 0; j < width / 2; j++) {
    64. for (int i = 1; i <= height / 2; i++) {
    65. *dst++ = *(src_v + i * width / 2 - j);
    66. }
    67. }
    68. }
    69. static void yuv420p_rotate(int8_t *dst, int8_t *src, int width, int height, int degree) {
    70. switch(degree) {
    71. case 0:
    72. memcpy(dst, src, width * height * 3 / 2);
    73. break;
    74. case 90:
    75. yuv420p_rotate90(dst, src, width, height);
    76. break;
    77. case 180:
    78. yuv420p_rotate180(dst, src, width, height);
    79. break;
    80. case 270:
    81. yuv420p_rotate270(dst, src, width, height);
    82. break;
    83. default:
    84. break;
    85. }
    86. }

  • 相关阅读:
    pom管理规范
    [附源码]计算机毕业设计JAVA疫情状况下生活物资集体团购系统
    微信小程序环境搭建
    支持JDK19虚拟线程的web框架,之二:完整开发一个支持虚拟线程的quarkus应用
    FANUC机器人Process IO接线及信号配置方法(一)
    Spring 作用域解析器AnnotationScopeMetadataResolver
    UnrealEngine源码下载
    推荐系统实现原理介绍
    仙人掌之歌——权力的游戏(2)
    nginx反向代理和负载均衡配置
  • 原文地址:https://blog.csdn.net/u011686167/article/details/126217050