OpenGL YUV 和 RGB 图像转换出现偏色问题怎么解决?

早上知识星球里的一位同学,遇到 yuv2rgb 偏色问题,这个问题比较典型,今天展开说一下。

OpenGL YUV 和 RGB 图像转换出现偏色问题怎么解决?

省流版

直接贴出来更精确的转换公式:

vec3 rgb2yuv(vec3 rgb) {
    float y =  0.257 * rgb.r + 0.504 * rgb.g + 0.098 * rgb.b;
    float u = -0.148 * rgb.r - 0.291 * rgb.g + 0.439 * rgb.b;
    float v =  0.439 * rgb.r - 0.368 * rgb.g - 0.071 * rgb.b;
    return vec3(y,u,v);
}


float y = texture2D(texture0, uv).r - 0.063;
float v = texture2D(texture1, uv).r - 0.502;
float u = texture2D(texture2, uv).r - 0.502;

vec3 yuv = vec3(y,u,v);

vec3 yuv2rgb(vec3 yuv) {
    float r = 1.164 * yuv.x + 1.596 * yuv.z;
    float g = 1.164 * yuv.x - 0.392 * yuv.y - 0.813 * yuv.z;
    float b = 1.164 * yuv.x + 2.017 * yuv.y;
    return vec3(r,g,b);
}

刨根问底版

理论上,rgb2yuv 和 yuv2rgb 的转换是可逆的,也就是说,它们可以完美地还原图像,不会引入信息损失,类似于纯粹的数学运算 1+2=3,3-2=1 。

但是在实际情况中,由于计算机表示的精度有限、采样误差以及浮点运算的限制,转换过程中会导致信息损失。但是这个误差要是控制在肉眼无法辨别的范围还是很容易的。

基于上面分析,偏色的根本原因其实就是转换时的精度误差,解决办法就是提高精度(小数点后多精确几位),让误差在人眼无法分辨的范围。

下面来做个试验,利用上面的公式,我们对一张图片反复做多次 rgb2yuv 和 yuv2rgb 转换,然后看下最终图像颜色的变化。

测试代码:

#iChannel0 "https://img-baofun.zhhainiao.com/pcwallpaper_ugc_mobile/static/2ddf8479959f1f3d9f52d0d561d281fe.jpg"

vec3 rgb2yuv(vec3 rgb) {
    float y =  0.257 * rgb.r + 0.504 * rgb.g + 0.098 * rgb.b;
    float u = -0.148 * rgb.r - 0.291 * rgb.g + 0.439 * rgb.b;
    float v =  0.439 * rgb.r - 0.368 * rgb.g - 0.071 * rgb.b;
    return vec3(y,u,v);
}

vec3 yuv2rgb(vec3 yuv) {
    float r = 1.164 * yuv.x + 1.596 * yuv.z;
    float g = 1.164 * yuv.x - 0.392 * yuv.y - 0.813 * yuv.z;
    float b = 1.164 * yuv.x + 2.017 * yuv.y;
    return vec3(r,g,b);
}

void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
    vec2 uv = fragCoord / iResolution.xy;

    float N = 4000.0;

    vec4 col = texture2D(iChannel0, uv);;

    if(uv.x > 0.5) {
        while(N > 0.0) {
            N--;
            vec3 yuv = rgb2yuv(col.rgb);
            col.rgb = yuv2rgb(yuv);
        }
    }

    fragColor = col;
}

N=10,只做 10 次 yuv 和 rgb 的来回转换,效果如下,这时肉眼已经无法区分颜色的误差。

OpenGL YUV 和 RGB 图像转换出现偏色问题怎么解决?

N=4000,做 4000 次 yuv 和 rgb 的来回转换放大误差,效果如下,这时由于误差不断累计,出现了明显的偏色。不过,转换 4000 次这种操作在实际情况下不太可能出现。

OpenGL YUV 和 RGB 图像转换出现偏色问题怎么解决?

另外,除了小数点后多精确几位,shader 里面的 float 也要声明为高精度:

precision highp float;

OpenGL ES 3.x GL_EXT_YUV_target 扩展,也提供了内置的颜色空间转换函数(推荐使用),精度更高,可以选择不同的转换标准,如:

    yuvCscStandardEXT conv_standard = itu_601;
    yuvCscStandardEXT conv_standard = itu_601_full_range;
    yuvCscStandardEXT conv_standard = itu_709;

贴一个源码展示下内置颜色空间转换函数使用方法。

#version 300 es
#extension GL_EXT_YUV_target: require
precision mediump float;
in vec2 v_texCoord;
layout(yuv) out vec4 outColor;
uniform sampler2D s_TextureMap;
void main()
{
    yuvCscStandardEXT conv_standard = itu_709;
    vec4 rgbaColor = texture(s_TextureMap, v_texCoord);
    vec3 rgbColor = vec3(rgbaColor.r, rgbaColor.g, rgbaColor.b);
    vec3 yuv = rgb_2_yuv(rgbColor, conv_standard);
    outColor = vec4(yuv, 1.0);
}

————————————————
参考链接:https://blog.csdn.net/Kennethdroid/article/details/133244034
参考链接:https://stackoverflow.com/questions/17892346/how-to-convert-rgb-yuv-rgb-both-ways
参考链接:https://registry.khronos.org/OpenGL/extensions/EXT/EXT_YUV_target.txt

— END —

进技术交流群,扫码添加我的微信:Byte-Flow

字节流动

本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/37806.html

(0)

相关推荐

发表回复

登录后才能评论