首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Opengl ES之RGB转NV21

前言

在上一篇理论文章中我们介绍了YUV到RGB之间转换的几种公式与一些优化算法,今天我们再来介绍一下RGB到YUV的转换,顺便使用Opengl ES做个实践,将一张RGB的图片通过Shader的方式转换YUV格式图,然后保存到本地。

可能有的童鞋会问,YUV转RGB是为了渲染显示,那么RGB转YUV的应用场景是什么?在做视频编码的时候我们可以使用MediaCodec搭配Surface就可以完成,貌似也没有用到RGB转YUV的功能啊,硬编码没有用到,那么软编码呢?一般我们做视频编码的时候都是硬编码优先,软编码兜底的原则,在遇到一些硬编码不可用的情况下可能就需要用到x264库进行软编码了,而此时RGB转YUV可能就派上用场啦。

RGB到YUV的转换公式

在前面 Opengl ES之YUV数据渲染 一文中我们介绍过YUV的几种兼容标准,下面我们看看RGB到YUV的转换公式:

RGB 转 BT.601 YUV

RGB 转 BT.709 YUV

或者也可以使用矩阵运算的方式进行转换,更加的便捷:

RGB转YUVRGB转YUV

先说一下RGB转YUV的过程,先将RGB数据按照公式转换为YUV数据,然后将YUV数据按照RGBA进行排布,这一步的目的是为了后续数据读取,最后使用读取YUV数据。

而对于OpenGL ES来说,目前它输入只认RGBA、lumiance、luminace alpha这几个格式,输出大多数实现只认RGBA格式,因此输出的数据格式虽然是YUV格式,但是在存储时我们仍然要按照RGBA方式去访问texture数据。

以NV21的YUV数据为例,它的内存大小为。如果是RGBA的格式存储的话,占用的内存空间大小是(因为 RGBA 一共4个通道)。很显然它们的内存大小是对不上的,那么该如何调整Opengl buffer的大小让RGBA的输出能对应上YUV的输出呢?我们可以设计输出的宽为,高为即可。

为什么是这样的呢?虽然我们的目的是将RGB转换成YUV,但是我们的输入和输出时读取的类型GLenum是依然是RGBA,也就是说:width x height x 4 = (width / 4) x (height * 3 / 2) * 4

而YUV数据在内存中的分布以下这样子的:

那么上面的排序如果进行了归一化之后呢,就变成了下面这样子了:

从上面的排布可以看出看出,在纹理坐标时,需要完成一次对整个纹理的采样,用于生成Y数据,当纹理坐标 时,同样需要再进行一次对整个纹理的采样,用于生成UV的数据。同时还需要将我们的视窗设置为

由于视口宽度设置为原来的 1/4 ,可以简单的认为相对于原来的图像每隔4个像素做一次采样,由于我们生成Y数据是要对每一个像素都进行采样,所以还需要进行3次偏移采样。

同理,生成对于UV数据也需要进行3次额外的偏移采样。

在着色器中offset变量需要设置为一个归一化之后的值:, 按照原理图,在纹理坐标 y < (2/3) 范围,一次采样(加三次偏移采样)4 个 RGBA 像素(R,G,B,A)生成 1 个(Y0,Y1,Y2,Y3),整个范围采样结束时填充好 大小的缓冲区;当纹理坐标 y > (2/3) 范围,一次采样(加三次偏移采样)4 个 RGBA 像素(R,G,B,A)生成 1 个(V0,U0,V0,U1),又因为 UV 缓冲区的高度为 height/2 ,VU plane 在垂直方向的采样是隔行进行,整个范围采样结束时填充好 大小的缓冲区。

主要代码

下面是Activity的主要代码逻辑:

以下是自定义SurfaceView的代码:

BaseOpengl的java代码:

将转换后的YUV数据读取保存好后,可以将数据拉取到电脑上使用这个软件查看是否真正转换成功。

参考

专栏系列

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20230116A00SWG00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券