如何使用GLSL正确解开V210视频帧?

 汉子r_843 发布于 2023-02-13 15:26

我有一个来自捕获卡的10位YUV(V210)视频帧,我想在GLSL着色器中解压缩这些数据并最终转换为RGB以进行屏幕输出.我在Linux上使用Quadro 4000卡(OpenGL 4.3).

我正在使用以下设置上传纹理:

视频帧:720x486像素

在128字节对齐的内存中物理占用933120个字节(步幅为1920)

纹理当前上传为480x486像素(步幅/ 4 x高度),因为这与数据的字节数匹配

GL_RGB10_A2的internalFormat

GL_RGBA的格式

GL_UNSIGNED_INT_2_10_10_10_REV的类型

过滤当前设置为GL_NEAREST

为清晰起见,这是upload命令:

int stride =((m_videoWidth + 47)/ 48)*128;

glTexImage2D(GL_TEXTURE_2D,0,GL_RGB10_A2,stride/4,m_videoHeight,0,GL_RGBA,GL_UNSIGNED_INT_2_10_10_10_REV,bytes);

数据本身包装如下:

UYVA | YUYA | VYUA | YVYA

或者在这里查看Blackmagic的插图:http://i.imgur.com/PtXBJbS.png

每个纹素总共为32位("R,G,B"通道各10位,alpha为2位).复杂的地方是将6个像素打包到128位的这个块中.这些块只是重复上述模式直到帧结束.

我知道可以使用texture2D(tex,coord).rgb访问每个纹素的组件,但由于每个纹素的顺序不相同(例如UYV vs YUY),我知道必须操纵纹理坐标来解释那.

但是,我不知道如何处理这样一个事实:这个纹理中只有比GL知道更多的像素,我相信这意味着我必须考虑放大/缩小以及min/mag过滤(我需要双线性)在我的着色器内部.输出窗口需要能够是任何大小(比纹理更小,相同或更大),因此着色器不应具有与之相关的任何常量.

我怎么能做到这一点?

2 个回答
  • 我建议先写一个只进行像素重新排序的着色器,并保持插值.

    它确实需要额外的视频RAM和另一个渲染通道,但它并不一定要慢:如果你包括重新缩放,你需要计算4个中间像素的内容,然后在它们之间进行插值.如果为插值创建单独的着色器,则可以像使用硬件插值返回单个纹理查找的结果一样简单.

    为颜色样本重新排列设置正确的着色器后,您始终可以将其转换为函数.

    那么如何编写重新排列着色器?

    您的输入如下所示:

      U Y V A | Y U Y A | V Y U A | Y V Y A
    

    让我们简单地假设您只想读Y.然后您可以制作一个非常简单的1d纹理(720列x 1行).每个纹理单元格都有两个值:列偏移从哪里读取值.其次,我们需要在该单元格中Y样本的位置:

      U Y V A | Y U Y A | V Y U A | Y V Y A
    
    
      0      1      2      3      4      5   ..... // out column (0-720)
    
      0      1      1      2      3      3   ..... // source column index (0-480)
      1      0      2      1      0      2   ..... // Y sample index in column (range 0-2)
    

    要获得Y(亮度)值,请使用屏幕x位置索引行纹理.然后你知道要读取哪个源纹素.然后取第二个组件,并使用它来获取正确的样本.在DirectX中,您只需vec4/float4使用整数索引a 即可选择R/G/B/A值.我希望GLSL支持同样的东西.

    所以现在你有了Y.对U和V重复上述过程.

    一旦你开始工作,你可以尝试通过巧妙地在一个纹理中更有效地包装上述信息来优化,而不是三个不同的纹理.或者,您可以尝试考虑线性函数,在舍入后生成列索引.这将为您节省许多纹理查找.

    但也许整个优化在你的场景中没有实际意义.只需让最简单的案例先行.

    我特意没有为你编写着色器代码,因为我对DirectX非常熟悉.但这应该让你开始.

    祝好运!

    2023-02-13 15:29 回答
  • 这是完成的着色器,包含所有通道和RGB转换(但不执行过滤):

    #version 130
    #extension GL_EXT_gpu_shader4 : enable
    in vec2 texcoord;
    uniform mediump sampler2D tex;
    out mediump vec4 color;
    
    // YUV offset
    const vec3 yuvOffset = vec3(-0.0625, -0.5, -0.5);
    
    // RGB coefficients
    // BT.601 colorspace
    const vec3 Rcoeff = vec3(1.1643,  0.000,  1.5958);
    const vec3 Gcoeff = vec3(1.1643, -0.39173, -0.81290);
    const vec3 Bcoeff = vec3(1.1643,  2.017,  0.000);
    
    // U Y V A | Y U Y A | V Y U A | Y V Y A
    
    int GROUP_FOR_INDEX(int i) {
      return i / 4;
    }
    
    int SUBINDEX_FOR_INDEX(int i) {
      return i % 4;
    }
    
    int _y(int i) {
      return 2 * i + 1;
    }
    
    int _u(int i) {
      return 4 * (i/2);
    }
    
    int _v(int i) {
      return 4 * (i / 2) + 2;
    }
    
    int offset(int i) {
      return i + (i / 3);
    }
    
    vec3 ycbcr2rgb(vec3 yuvToConvert) {
      vec3 pix;
      yuvToConvert += yuvOffset;
      pix.r = dot(yuvToConvert, Rcoeff);
      pix.g = dot(yuvToConvert, Gcoeff);
      pix.b = dot(yuvToConvert, Bcoeff);
      return pix;
    }
    
    void main(void) {
      ivec2 size = textureSize2D(tex, 0).xy; // 480x486
      ivec2 sizeOrig = ivec2(size.x * 1.5, size.y); // 720x486
    
      // interpolate 0,0 -> 1,1 texcoords to 0,0 -> 720,486
      ivec2 texcoordDenorm = ivec2(texcoord * sizeOrig);
    
      // 0 1 1 2 3 3 4 5 5 6 7 7 etc.
      int yOffset = offset(_y(texcoordDenorm.x));
      int sourceColumnIndexY = GROUP_FOR_INDEX(yOffset);
    
      // 0 0 1 1 2 2 4 4 5 5 6 6 etc.
      int uOffset = offset(_u(texcoordDenorm.x));
      int sourceColumnIndexU = GROUP_FOR_INDEX(uOffset);
    
      // 0 0 2 2 3 3 4 4 6 6 7 7 etc.
      int vOffset = offset(_v(texcoordDenorm.x));
      int sourceColumnIndexV = GROUP_FOR_INDEX(vOffset);
    
      // 1 0 2 1 0 2 1 0 2 etc.
      int compY = SUBINDEX_FOR_INDEX(yOffset);
    
      // 0 0 1 1 2 2 0 0 1 1 2 2 etc.
      int compU = SUBINDEX_FOR_INDEX(uOffset);
    
      // 2 2 0 0 1 1 2 2 0 0 1 1 etc.
      int compV = SUBINDEX_FOR_INDEX(vOffset);
    
      vec4 y = texelFetch(tex, ivec2(sourceColumnIndexY, texcoordDenorm.y), 0);
      vec4 u = texelFetch(tex, ivec2(sourceColumnIndexU, texcoordDenorm.y), 0);
      vec4 v = texelFetch(tex, ivec2(sourceColumnIndexV, texcoordDenorm.y), 0);
    
      vec3 outColor = ycbcr2rgb(vec3(y[compY], u[compU], v[compV]));
    
      color = vec4(outColor, 1.0);
    }
    

    如果图像将在屏幕上放大,那么您可能希望进行双线性过滤,但这需要在着色器中执行.

    2023-02-13 15:29 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有