OpenGL实现相机视频NV21格式转RGB格式

  • 时间:2017-07-20
  • 分类:数据库/系统
  • 1508 人浏览
[导读]最近公司项目做人脸识别追踪,从相机出来的视频格式是YUV420或者说是NV21格式,在面板上显示出来的使用的是RGB格式,所以要将其转化成该格式,下面先了解一下YUV420格式,网上很多这方面的介绍,理解起来很麻烦,本篇博客通过使用简介的语言希望能够帮助读者更好的理解
最近公司项目做人脸识别追踪,从相机出来的视频格式是YUV420或者说是NV21格式,在面板上显示出来的使用的是RGB格式,所以要将其转化成该格式,下面先了解一下YUV420格式,网上很多这方面的介绍,理解起来很麻烦,本篇博客通过使用简介的语言希望能够帮助读者更好的理解:

视频数据也是一张张图片组成的,每张图片的大小是由图片的(width * height)*3/2字节组成。图片分两部分:Y通道的长度是width * height。UV平面字节长度是:(width / 2) x (height / 2) x 2 = width x height / 2 。每两个连续的字节是2 x 2 = 4个原始像素的V,U(按照NV21规范的顺序)色度字节。换句话说,UV平面尺寸为(宽/ 2)×(高/ 2)像素,并且在每个维度中被下采样因子2, 此外,U,V色度字节是交错的。

下面给读者展示一副关于YUV-NV12, NV21存储的图片:


接下来介绍如何将其转化成RGB

如问题所述,如果在Android代码中完成,此转换将需要太多时间才能生效。 幸运的是,它可以在GPU上运行的GL着色器中完成。 这将允许它运行非常快。

一般的想法是将我们的图像的纹理作为纹理传递给着色器,并以RGB转换的方式渲染它们。 为此,我们必须首先将图像中的通道复制到可传递给纹理的缓冲区中:

然后,我们将这些缓冲区传递给实际的GL纹理:


接下来,我们渲染之前准备的网格(覆盖整个屏幕), 着色器将负责渲染网格上的绑定纹理:

最后,着色器接管将纹理渲染到网格的任务, 实现实际转换的片段着色器如下所示:

请注意,我们使用相同的坐标变量v_texCoord访问Y和UV纹理,这是由于v_texCoord在-1.0和1.0之间,从纹理的一端到另一端,而不是实际的纹理像素坐标, 这是着色器最好的功能之一。

最后为了方便读者学习,给出完整的代码:

由于libgdx是跨平台的,因此我们需要一个可以在处理设备摄像头和渲染的不同平台中进行不同扩展的对象。 例如,如果您可以让硬件为您提供RGB图像,则可能需要绕过YUV-RGB着色器转换。 因此,我们需要一个将由每个不同平台实现的设备摄像头控制器接口:

该界面的Android版本如下(实时摄像机图像假定为1280x720像素):

主应用程序部分只是确保在开始时调用一次init(),renderBackground()每帧渲染循环,并且destroy()最后调用一次:

唯一的其他Android特定部分是以下非常短的主要Android代码,您只需创建一个新的Android特定设备相机处理程序并将其传递到主要的libgdx对象:

运行速度是非常快的,测试结果如下:

三星Galaxy Note II LTE - (GT-N7105):拥有ARM Mali-400 MP4 GPU。
渲染一帧需要大约5-6毫秒,每隔几秒偶尔会跳到15 ms左右
实际渲染线(mesh.render(着色器,GL20.GL_TRIANGLES);)一致地需要0-1 ms
两个纹理的创建和绑定总共需要1-3毫秒
ByteBuffer副本通常需要1-3毫秒,但偶尔跳到大约7ms,这可能是由于图像缓冲区在JVM堆中移动。


三星Galaxy Note 10.1 2014 - (SM-P600):具有ARM Mali-T628 GPU。
渲染一帧需要大约2-4毫秒,罕见的跳到大约6-10毫秒
实际渲染线(mesh.render(着色器,GL20.GL_TRIANGLES);)一致地需要0-1 ms
两个纹理的创建和绑定总共需要1-3毫秒,但每两秒钟跳到大约6-9毫秒
ByteBuffer副本通常总共需要0-2 ms,但很少跳到大约6ms

另外将Shader的顶点和片段着色器展现如下:






来源:本文为线上采编,如涉及作品内容、版权和其它问题,请及时与本网联系,我们将在第一时间删除!