我编写了一个WebGL着色器,它通过3个不同的纹理单元呈现yuv420p图片(因此它也可以处理平面422,444 .)。您可以在浏览器这里上运行它。这里是我转换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(这个wiki页面)的系数矩阵。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手机上和我的电脑上看起来有点不同。同样的情况也发生在Linux和Windows浏览器上。当我测量右下角黑匣子的颜色时,我在站台/hw上得到了“不同的黑色”。
有什么办法缓解这种情况吗?为什么我不能得到“真正的”黑色(#000000)?
额外位
我不只是担心这一点,因为有些人确实注意到了这种差异。有一天,当我在我的办公室工作时,这位客户坚持我的视频播放器的颜色与另一家公司的程序不同。没有人知道为什么(没有其他程序的源代码)。但似乎其他程序在RGB表面上呈现,而我的程序则在YUV DirectX表面上呈现。所以他们必须从CPU进行转换,如果是这样的话。它看起来不同的原因可能有很多。可能是颜色范围一些吉米用的坏系数..。名单可能会继续下去。
发布于 2017-09-18 04:54:22
你在假设你的色度样本位置在哪里,它并不总是紫外线纹理的中间位置。
你是线性插值的YUV样本,仍然是在非线性伽马。
YUV的色域大于RGB的色域(您将得到0到1 RGB范围之外的值),因此您需要剪辑或重新映射颜色值。
并不是所有的图形堆栈都正确地处理RGB输出伽马,所以您可能会发现GPU发射的伽马和显示器如何解释它之间的不匹配。
另外,你确定你得到了一个BT.709输入吗?其他格式的色度常数不同,即使内存中的像素填充格式是相同的(无论是恢复RGB,还是使用窄或宽的色域)。
https://stackoverflow.com/questions/46244345
复制相似问题