前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >OpenGL YUV 和 RGB 图像转换出现偏色问题怎么解决?

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

作者头像
字节流动
发布2023-11-17 16:35:36
5010
发布2023-11-17 16:35:36
举报
文章被收录于专栏:字节流动字节流动

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

省流版

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

代码语言:javascript
复制
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 转换,然后看下最终图像颜色的变化。

测试代码:

代码语言:javascript
复制
#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 的来回转换,效果如下,这时肉眼已经无法区分颜色的误差。

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

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

代码语言:javascript
复制
precision highp float;

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

代码语言:javascript
复制
    yuvCscStandardEXT conv_standard = itu_601;
    yuvCscStandardEXT conv_standard = itu_601_full_range;
    yuvCscStandardEXT conv_standard = itu_709;

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

代码语言:javascript
复制
#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 --

获取相关资料和源码

推荐:

Android FFmpeg 实现带滤镜的微信小视频录制功能

全网最全的 Android 音视频和 OpenGL ES 干货,都在这了

一文掌握 YUV 图像的基本处理

抖音传送带特效是怎么实现的?

所有你想要的图片转场效果,都在这了

面试官:如何利用 Shader 实现 RGBA 到 NV21 图像格式转换?

我用 OpenGL ES 给小姐姐做了几个抖音滤镜

项目疑难问题解答、大厂内部推荐、面试指导、简历指导、代码指导、offer 选择建议、学习路线规划,可以点击找我一对一解答。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-11-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 字节流动 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 省流版
  • 刨根问底版
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档