一文读懂rawRGB、RGB和YUV数据格式与转换

rawRGB     

        图像采集的过程为:光照在成像物体被反射 -> 镜头汇聚 -> Sensor光电转换-> ADC转换为rawRGB

因为sensor上每个像素只采集特定颜色的光的强度,因此sensor每个像素只能为R或G或B,形成的数据就成为了rawRGB数据。

         rawRGB数据是sensor的经过光电转换后通过ADC采样后直接输出数据,是未经处理过的数据,表示sensor接受到的各种光的强度。

         对于不同的sensor,在其内部形成的rawRGB数据格式也是有区别的。rawRGB数据排列格式有四种如下表(这里的格式是对于2*2像素矩阵而言的):

 

        假设一个sensor的像素是8*8(分辨率为8*8),那么这个sensor就有8*8个感光点,每个感光点就是一个晶体管。那么对于上表中四种排列格式的rawRGB数据如下图所示:

        由上图可以看出,每一种格式的rawRGB数据的G分量都是B、R分量的两倍,是因为人眼对于绿色的更加敏感,所以加重了其在感光点的权重,增加了对绿色信息的采样。

         对于sensor输出的rawRGB数据,需要送到ISP(图像信号处理)中处理,得到RGB数据,一般采用插值处理。在进行ISP处理时,ISP需要知道sensor输出的rawRGB数据的顺序与大小,其中顺序一般通过配置ISP的pattern寄存器来实现,大小一般配置在ISP的输入格式控制寄存器中。

         那么,这里还有一个问题:在rawRGB数据中,每个像素只有R、G、B颜色三分量中一个分量,那么这一个分量用多少bit来表示呢?答案如下表:

        之所以有个rawRGB这种格式的数据,这样做的目的一般是为了降低感光器件的物理工艺难度,然后通过ISP处理还原出更真实的图像信息。

RGB

        在数字化的时代,需要一种标准来量化自然界的各种颜色。RGB就是一种在数字化领域表示颜色的标准,也称作一种色彩空间,通过用三原色R、G、B的不同的亮度值组合来表示某一种具体的颜色。注意,RGB里面存的是颜色的亮度值,而不是色度值。

        在实际应用中,RGB存在着许多的格式,之所以存在着这些格式,是因为随着技术的进步,系统的更迭,不同的应用场景和设备环境,对颜色表达的需求是不同的。

        常用的RGB格式如下表所示:

格式

描述

RGB565

1. 每个像素用16位表示,RGB分量分别使用5位、6位、5位。

2. 内存中排列(高字节->低字节):R R R R R G G G G G G B B B B B

RGB555

1. 每个像素用16位表示,RGB分量都使用5位(剩下1位不用)

2. 内存中排列(高字节->低字节):X R R R R G G G G G B B B B B(X表示不用,可以忽略)

RGB24(RGB888)

1. 每个像素用24位表示,RGB分量各使用8位。在内存中RGB各分量的排列顺序为:BGR BGR BGR ......

2. 内存中排列(高字节->低字节):B B B B B B B B G G G G G G G G R R R R R R R R

RGB32

1. 每个像素用32位表示,RGB分量各使用8位(剩下8位不用)

2. 内存中排列(高字节->低字节):B B B B B B B B G G G G G G G G R R R R R R R R X X X X X X X X (X表示不用,可以忽略)

ARGB32

1. 每个像素用32位表示,RGB分量各使用8位(剩下的8位用于表示Alpha通道值)

2. 内存中排列(高字节->低字节):B B B B B B B B G G G G G G G G R R R R R R R R A A A A A A A A

YUV

        YUV是一种色彩编码方法,Y表示亮度,U和V表示色度。只有Y就是黑白图像,再加上UV就是彩色图像了。YUV的一个好处就是让彩色系统和传统黑白系统很好的兼容,同时利用了人类视觉系统对亮度的敏感度比对色度高。

        在一般应用中,人们所说的YUV就是YCbCr,这我认为是从狭义的角度理解(毕竟现在是数字信号的天下,YCbCr恰好是描述数字信号的)。广义上说,YUV是一种色彩空间分类,一种颜色模型,具体的类型有Y'UV, YUV, YCbCr,YPbPr等,目前市场上常用数字的视频信号和视频/图片文件中的编码格式,用YCbCr来描述,于是人们口中的YUV就代指的是YCbCr,常见应用如H.264/H.265码流、MPEG、JPEG等。

        YCbCr中的Cb指蓝色色度分量,而Cr指红色色度分量。

        YUV和RGB的相同点:都是用来表达颜色的数学方法;

        YUV和RGB的相同点:对颜色的描述思路和方法不同。RGB将一个颜色拆解为3个纯色的亮度组合,YUV将一个颜色分解为一个亮度和2个色度的组合。

        那引入YUV这种色彩空间的好处具体有啥呢?

 1.   YUV提取Y亮度信号,可以直接给黑白电视使用,兼容黑白电视

 2.  人眼对UV的敏感性小于亮度,这样我们适当减少uv的量,而不影响人的感官。所以才会有多种格式的 YUV描述,如420、422、444。

3.  伴随显示设备分辨率的提升,bt组织也针对yuv2rgb设定了不同的系数,来最好的从YUV转换到RGB。

4.  YUV格式可以比RGB格式储存空间小。

        那RGB存在的作用是什么呢?

        目前人类发明的所有彩色的输入输出设备,本质上都只支持RGB数据。哪怕设备允许YUV的输入输出,那也是经过内部的数据转换而间接支持。

YUV采样

        主流采样方式有如下三种:

                   YUV 4:4:4 采样

                   YUV 4:2:2 采样

                   YUV 4:2:0 采样

        1.YUV 444 采样

        全采样,对每个像素点的的YUV分量都进行采样,这样的三个分量信息量完整。

        2. YUV 422 采样

        部分采样,可节省1/3存储空间和1/3的数据传输量。UV分量是Y分量采样的一半,Y分量和UV 分量按照2 : 1的比例采样。如果水平方向有10个像素点,那么采样了10个Y分量,而只采样了5个UV分量。其中,每采样过一个像素点,都会采样其Y分量,而U、V分量就会间隔一个采集一个。

        3.YUV 420 采样

        部分采样,可节省1/2存储空间和1/2的数据传输量。YUV 420采样,并不是指只采样U分量而不采样V分量。而是指,在每一行扫描时,只扫描一种色度分量(U或者V),和Y分量按照2 : 1的方式采样。比如,第一行扫描时,YU 按照 2 : 1的方式采样,那么第二行扫描时,YV分量按照 2:1的方式采样。对于每个色度分量来说,它的水平方向和竖直方向的采样和Y分量相比都是2:1 。

YUV存储格式

        YUV存储格式通常有两大类:打包(packed)格式和平面(planar)格式。前者将YUV分量存放在同一个数组中,通常是几个相邻的像素组成一个宏像素(macro-pixel);而后者使用三个数组分开放 YUV 三个分量,就像是一个三维平面一样。

YUV422 Planar (YUV422P,也称为I422格式)

        这里,Y\U\V数据是分开存放的,每两个水平Y采样点,有一个U和一个V采样点,如下图:

        也就是说,U0V0由Y0、Y1共用,这样整幅图像较RGB就减少了1/3的存储空间。

        YUV 422P 格式,又叫做 I422格式,采用的是平面格式进行存储,先存储所有的 Y 分量,再存储所有的 U 分量,再存储所有的 V 分量。

        假如一个8*2像素的图像的该格式的存储分布如下图:

YUV422 packed

        此格式有两种情况:分为YUYV格式和UYVY格式。

        YUYV格式

               YUYV 格式是采用打包格式进行存储的,指每个像素点都采用 Y 分量,但是每隔一个像素采样它的UV分量。

               假如一个8*2像素的图像的该格式的存储分布如下图:

        UYVY格式

                UYVY 格式也是采用打包格式进行存储,它的顺序和YUYV相反,先采用U分量再采样Y分量。

                假如一个8*2像素的图像的该格式的存储分布如下图:

YUV420 Planar (YUV420P)

        这个格式跟YUV422 Planar 类似,但对于U和V的采样在水平和垂直方向都减少为2:1,根据采样规则如下图:

        也就是说,U0V0由Y0、Y1、YW、YW+1共用,这样整幅图像较RGB就减少了1/2的存储空间。

        YU12和YV12格式都属于YUV 420P类型,即先存储Y分量,再存储U、V 分量,区别在于:YU12是先Y再U后V,而YV12是先Y再V后U 。

        YU12格式(也称为I420格式)

                YU12是先Y再U后V。

                假如一个8*2像素的图像的该格式的存储分布如下图:

        YV12格式

                YV12是先Y再V后U

                假如一个8*2像素的图像的该格式的存储分布如下图:

YUV422 Semi-Planar (YUV422SP)

        Semi 是“半”的意思,个人理解这个是半平面模式,这个格式的数据量跟YUV422 Planar的一样,但是U、V是交叉存放的。                                              

        假如一个8*2像素的图像的该格式的存储分布如下图:

YUV420 Semi-Planar (YUV420SP)

        这个格式的数据量跟YUV420 Planar的一样,但是U、V是交叉存放的。

        NV12和NV21格式都属于 YUV420SP 类型。它也是先存储了Y分量,但接下来并不是再存储所有的U或者V分量,而是把UV分量交替连续存储。

        NV12

                NV12是IOS中有的模式,它的存储顺序是先存Y分量,再UV进行交替存储。

                假如一个8*2像素的图像的该格式的存储分布如下图:

        NV21

                NV21是安卓中有的模式,它的存储顺序是先存Y分量,在VU交替存储。

                假如一个8*2像素的图像的该格式的存储分布如下图:

RGB与YUV转换

        由于YUV有这数字信号和模拟信号的YUV类型,于是RGB与YUV转换方式多种多样,大致分为模拟和数字两种。每种方式下,不同的清晰度视频信号的转换公式也是有所不同。

        这里只讨论了数字信号的转换,数字RGB转YUV的推导公式为:

                Wr + Wg + Wb = 1; Umax = Vmax = 0.5;                       (1)

                Y = 16 + 219 * (Wr * R + Wg * G + Wb * B) / 255;              (2)

                Cb = U = 128 + 224 * (Umax * (B - Y) / (1 - Wb)) / 255;         (3)

                Cr = V = 128 + 224 * (Vmax * (R - Y) / (1 - Wr)) / 255;        (4)

        注意:(1)(2)(3)(4)公式中WrWgWb  的值如下表所示:

清晰度WrWgWb
BT601(标清国际定义)0.2990.5870.114
BT709(高清)0.21260.71520.0722
BT2020(超清)0.26270.6780.0593

这里仅仅列出数字YUV(YCbCr)与数字RGB相互转换的BT601(标清国际定义)的转换公式:

         Y = 16 + 0.257 * R + 0.504 * g + 0.098 * b

        Cr = V = 128 + 0.439 * R - 0.368 * g - 0.071 * b

        Cb = U = 128 - 0.148 * R - 0.291 * g + 0.439 * b

 

        B = 1.164 *(Y - 16) + 2.016 *(Cb - 128)

        G = 1.164 *(Y - 16) - 0.392 *(Cb - 128) - 0.812 *(Cr - 128)

        R = 1.164 *(Y - 16) + 1.596 *(Cr - 128)

 

  • 44
    点赞
  • 300
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
在OpenGL ES中将YUV数据转换RGB格式需要进行以下步骤: 1. 创建一个纹理对象,用于存储转换后的RGB数据。 2. 将YUV数据绑定到一个纹理单元上。 3. 创建一个着色器程序,用于将YUV数据转换RGB格式。 4. 将纹理对象绑定到FBO(Frame Buffer Object),并将着色器程序与FBO绑定。 5. 渲染FBO以将YUV数据转换RGB格式。 下面是一个简单的代码示例: 1. 创建纹理对象 ``` GLuint textureID; glGenTextures(1, &textureID); glBindTexture(GL_TEXTURE_2D, textureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); ``` 2. 绑定YUV数据到纹理单元 ``` glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, textureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, yData); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, textureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, width / 2, height / 2, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, uvData); ``` 3. 创建着色器程序 ``` const char* vertexShader = "attribute vec4 position;\n" "attribute vec2 texCoord;\n" "varying vec2 v_texCoord;\n" "void main()\n" "{\n" " gl_Position = position;\n" " v_texCoord = texCoord;\n" "}\n"; const char* fragmentShader = "precision highp float;\n" "varying vec2 v_texCoord;\n" "uniform sampler2D yTexture;\n" "uniform sampler2D uvTexture;\n" "void main()\n" "{\n" " float y = texture2D(yTexture, v_texCoord).r;\n" " float u = texture2D(uvTexture, v_texCoord).r - 0.5;\n" " float v = texture2D(uvTexture, v_texCoord).a - 0.5;\n" " float r = y + 1.13983 * v;\n" " float g = y - 0.39465 * u - 0.58060 * v;\n" " float b = y + 2.03211 * u;\n" " gl_FragColor = vec4(r, g, b, 1.0);\n" "}\n"; GLuint programID = glCreateProgram(); GLuint vertexShaderID = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShaderID, 1, &vertexShader, NULL); glCompileShader(vertexShaderID); glAttachShader(programID, vertexShaderID); GLuint fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShaderID, 1, &fragmentShader, NULL); glCompileShader(fragmentShaderID); glAttachShader(programID, fragmentShaderID); glLinkProgram(programID); glUseProgram(programID); ``` 4. 绑定纹理对象到FBO,并将着色器程序与FBO绑定 ``` GLuint fboID; glGenFramebuffers(1, &fboID); glBindFramebuffer(GL_FRAMEBUFFER, fboID); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureID, 0); glViewport(0, 0, width, height); glUseProgram(programID); glUniform1i(glGetUniformLocation(programID, "yTexture"), 0); glUniform1i(glGetUniformLocation(programID, "uvTexture"), 1); ``` 5. 渲染FBO ``` GLfloat vertices[] = { -1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 0.0f }; GLubyte indices[] = {0, 1, 2, 3}; glVertexAttribPointer(glGetAttribLocation(programID, "position"), 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), vertices); glVertexAttribPointer(glGetAttribLocation(programID, "texCoord"), 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), vertices + 2); glEnableVertexAttribArray(glGetAttribLocation(programID, "position")); glEnableVertexAttribArray(glGetAttribLocation(programID, "texCoord")); glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, indices); glDisableVertexAttribArray(glGetAttribLocation(programID, "position")); glDisableVertexAttribArray(glGetAttribLocation(programID, "texCoord")); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值