我写了一个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。
有什么方法可以缓解这种情况?为什么我不能得到“真正的”黑色(#000000)?
额外位
我不仅仅关心这一点,因为有些人确实发现了这种差异。有一天,当我在办公室工作时,这个客户坚持认为我的视频播放器的颜色与其他公司的程序不同。没有人知道为什么(没有其他程序的源代码)。但是当我的程序在YUV DirectX表面上渲染时,似乎另一个程序在RGB表面上渲染。因此,如果是这种情况,他们必须从CPU进行转换。可能有很多原因导致它看起来不同。可能是吉米使用的颜色范围,坏系数...列表可以继续。
答案 0 :(得分:0)
您正在假设色度样本网站的位置;它并不总是UV纹理的中间部分。
您正在线性插值仍处于非线性伽玛的YUV样本。
YUV的色域大于RGB的色域(您将获得0到1 RGB范围之外的值),因此您需要剪切或重新映射颜色值。
并非所有图形堆栈都能正确处理RGB输出伽玛,因此您可能会发现GPU发出的伽玛与显示器解释它之间的不匹配。
另外,您确定要获得BT.709输入吗?其他格式的色度常数不同,即使内存中的像素打包格式相同(在恢复RGB和使用窄色域或宽色域方面)