不同的GL着色器来自不同的平台/ hw(YUV到RGB)

时间:2017-09-15 16:53:01

标签: video opengl-es webgl yuv

我写了一个WebGL着色器,通过3个独立的纹理单元渲染yuv420p图片(因此它也可以处理平面422,444 ......)。您可以从浏览器here运行它。而here是我转换yuv文件的原始图像。

我用:

创建了YUV文件
ffmpeg -i color-test.jpg -pix_fmt yuvj420p -f rawvideo color-test.yuv

我明确使用yuvj420p代替yuv420p来获取全范围值(0-255)。因此,如果我在生产中使用此代码,我会添加一些制服来调整颜色范围。

顶点着色器

attribute vec3 a_pos;
attribute vec2 a_uv;

uniform mat4 u_mvp;

varying vec2 v_uv;


void main () {
  gl_Position = u_mvp * vec4(a_pos, 1.0);
  v_uv = a_uv;
}

片段着色器

precision mediump float;

uniform sampler2D u_texY;
uniform sampler2D u_texU;
uniform sampler2D u_texV;
uniform vec4 u_diffuse;

varying vec2 v_uv;

// YUV to RGB coefficient matrix.
const mat3 coeff = mat3(
  1.0, 1.0, 1.0,
  0.0, -0.21482, 2.12798,
  1.28033, -0.38059, 0.0
);


void main () {
  vec3 yuv = vec3(texture2D(u_texY, v_uv).a, texture2D(u_texU, v_uv).a, texture2D(u_texV, v_uv).a) * 2.0 - 1.0;
  vec3 colour = (coeff * yuv + 1.0) / 2.0;

  gl_FragColor = u_diffuse * vec4(colour, 1.0);
}

系数矩阵是BT.709 rec(this wiki page)的矩阵。 u_diffuse通常为vec4(1.0, 1.0, 1.0, 1.0),除非您想要使用特定颜色对图片进行漫反射。

纹理上传

像这样上传纹理(第233行):

  gl.activeTexture(gl.TEXTURE0);
  gl.bindTexture(gl.TEXTURE_2D, tex.y);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.ALPHA, rp.yuv.meta.width, rp.yuv.meta.height, 0, gl.ALPHA, gl.UNSIGNED_BYTE, picture.y);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

CLAMP_TO_EDGE用于处理图像的任何维度(任何可被2整除的维度)。请注意,每次绘制回调都会更新纹理。这是故意的,因为通常我们每次从视频解码器移交图片时都必须更新纹理。

问题

最明显的问题是渲染图像与图像查看器应用程序(即Web浏览器)的再现明显不同。这些程序可能使用CPU将图像从YUV缩放到RGB。我想这是最准确的转换方式。是什么原因导致我的程序?我做了我的研究,但我无法做出明确的解释。浮点精度?

问题进一步扩大。不同设备的颜色不同。例如,这个女人在我的Android手机上看起来与我的PC略有不同。在Linux和Windows浏览器上也会发生同样的事情。当我测量右下角黑匣子的颜色时,我在平台上得到了“不同的黑色”/ hw。

  • Linux上的Chrome:#060106
  • Linux上的Firefox:#010001
  • Android上的Chrome:#060006
  • Windows上的Chrome / Firefox / Oprea:#060106

有什么方法可以缓解这种情况?为什么我不能得到“真正的”黑色(#000000)?

额外位

我不仅仅关心这一点,因为有些人确实发现了这种差异。有一天,当我在办公室工作时,这个客户坚持认为我的视频播放器的颜色与其他公司的程序不同。没有人知道为什么(没有其他程序的源代码)。但是当我的程序在YUV DirectX表面上渲染时,似乎另一个程序在RGB表面上渲染。因此,如果是这种情况,他们必须从CPU进行转换。可能有很多原因导致它看起来不同。可能是吉米使用的颜色范围,坏系数...列表可以继续。

1 个答案:

答案 0 :(得分:0)

您正在假设色度样本网站的位置;它并不总是UV纹理的中间部分。

您正在线性插值仍处于非线性伽玛的YUV样本。

YUV的色域大于RGB的色域(您将获得0到1 RGB范围之外的值),因此您需要剪切或重新映射颜色值。

并非所有图形堆栈都能正确处理RGB输出伽玛,因此您可能会发现GPU发出的伽玛与显示器解释它之间的不匹配。

另外,您确定要获得BT.709输入吗?其他格式的色度常数不同,即使内存中的像素打包格式相同(在恢复RGB和使用窄色域或宽色域方面)