当前位置: 首页 > news >正文

OpenGL ES 相机 LUT 滤镜

OpenGLES 相机 LUT 滤镜

左侧为 LUT 滤镜效果,右侧为原图

什么是 LUT ? LUT 是 Look Up Table 的简称,称作颜色查找表,是一种针对色彩空间的管理和转换技术。

它可以分为一维 LUT(1D LUT) 和 三维 LUT(3D LUT),其中三维 LUT 比较常用。简单来讲,LUT 就是一个 RGB 组合到另一个 RGB 组合的映射关系表。

LUT(R, G, B) = (R1, G1, B1)

LUT 滤镜是一种比较经典的滤镜,本质上属于独立像素点替换,即根据 OpenGL 采样器对纹理进行采样得到的像素点,再基于像素点的(R,G,B)分量查表,获得 LUT 映射的(R1,G1,B1),替换原来的输出。

一般 RGB 像素占用 3 个字节,包含 3 个分量,每个分量有 256 种取值,那么三维 LUT 模板就可以包含 256 X 256 X 256 种情况,占用 48MB 内存空间。

这样一个 LUT 模板内存占用过大同时也降低了查找的效率,通常会采取下采样方式来降低数据量。

例如可以对三维 LUT 模板每个分量分别进行 64 次采样,这样就获得一个 64 X 64 X 64 大小的映射关系表,对于不在表内的颜色值可以进行插值获得其相似结果。

三维 LUT 模板,即64 X 64 X 64 大小的映射关系表,通常是用一张分辨率为 512 X 512 的二维图片表示,称为 LUT 图(模板图)。

三维 LUT 模板

LUT 图在横竖方向上被分成了 8 X 8 一共 64 个小方格,每一个小方格内的 B(Blue)分量为一个定值,64 个小方格一共表示了 B 分量的 64 种取值。

对于每一个小方格,横竖方向又各自分为 64 个小格,以左下角为原点,横向小格的 R(Red)分量依次增加,纵向小格的 G(Green)分量依次增加。

Lut 图中的一个小方格

至此,我们可以根据原始采样像素 RGB 中的 B 分量值,确定我们要选用 LUT 图中的第几个小格,然后再根据(R,G)分量值为纵横坐标,确定映射的 RGB 组合。

OpenGLES 实现 LUT 滤镜的 GLSL 脚本。

// Lut 滤镜
#version 100
precision highp float;
varying vec2 v_texcoord;

//Lut 纹理
uniform sampler2D s_LutTexture;
uniform lowp sampler2D s_textureY;
uniform lowp sampler2D s_textureU;
uniform lowp sampler2D s_textureV;

vec4 YuvToRgb(vec2 uv) {
    float y, u, v, r, g, b;
    y = texture2D(s_textureY, uv).r;
    u = texture2D(s_textureU, uv).r;
    v = texture2D(s_textureV, uv).r;
    u = u - 0.5;
    v = v - 0.5;
    r = y + 1.403 * v;
    g = y - 0.344 * u - 0.714 * v;
    b = y + 1.770 * u;
    return vec4(r, g, b, 1.0);
}

void main()
{
    //原始采样像素的 RGBA 值
    vec4 textureColor = YuvToRgb(v_texcoord);

    //获取 B 分量值,确定 LUT 小方格的 index, 取值范围转为 0~63
    float blueColor = textureColor.b * 63.0;

    //取与 B 分量值最接近的 2 个小方格的坐标
    vec2 quad1;
    quad1.y = floor(floor(blueColor) / 8.0);
    quad1.x = floor(blueColor) - (quad1.y * 8.0);

    vec2 quad2;
    quad2.y = floor(ceil(blueColor) / 7.9999);
    quad2.x = ceil(blueColor) - (quad2.y * 8.0);

    //通过 R 和 G 分量的值确定小方格内目标映射的 RGB 组合的坐标,然后归一化,转化为纹理坐标。
    vec2 texPos1;
    texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);
    texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g);

    vec2 texPos2;
    texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);
    texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g);

    //取目标映射对应的像素值
    vec4 newColor1 = texture2D(s_LutTexture, texPos1);
    vec4 newColor2 = texture2D(s_LutTexture, texPos2);

    //使用 Mix 方法对 2 个边界像素值进行混合
    vec4 newColor = mix(newColor1, newColor2, fract(blueColor));
    gl_FragColor = mix(textureColor, vec4(newColor.rgb, textureColor.w), 1.0);
}

LUT 滤镜对比图

LUT 滤镜对比图


技术交流,欢迎加我微信:ezglumes ,拉你入技术交流群。

推荐阅读:

音视频面试基础题

OpenGL ES 学习资源分享

一文读懂 YUV 的采样与格式

OpenGL 之 GPUImage 源码分析

推荐几个堪称教科书级别的 Android 音视频入门项目

觉得不错,点个在看呗~

相关文章:

  • Android 11 正式发布 | 开发者们的舞台已就绪
  • 刚刚,鸿蒙 OS 2.0 发布!HarmonyOS 将正式开源!
  • 如何给 FFmpeg 添加自定义 Codec 编码器
  • FFmpeg从入门到精通——进阶篇,SEI那些事儿
  • iOS音频采集技术解读:如何实现男女变声的音效?
  • MediaCodec 、x264、faac 实现音视频编码并通过 rtmp 协议实现推流
  • 从《黑神话:悟空》的爆火,浅谈当前游戏从业者面临的机遇与挑战
  • 面试官: 说一下你做过哪些性能优化?
  • NDK系列-如何使用C/C++编写带EGL功能的NativeActivity
  • 短视频 SDK 开发 (一) 开发一款短视频 SDK 需要具备哪些知识?
  • 滴滴AR实景导航背后的技术
  • 国庆假期归来,音视频继续搞起,WebRTC送书活动来啦~~~
  • 「字节跳动直播研发团队」是如何每天护航百万直播间的?
  • FFmpeg代码架构
  • 播放器性能优化干货
  • 【css3】浏览器内核及其兼容性
  • 03Go 类型总结
  • CentOS 7 修改主机名
  • egg(89)--egg之redis的发布和订阅
  • JavaScript 奇技淫巧
  • Storybook 5.0正式发布:有史以来变化最大的版本\n
  • Vue学习第二天
  • 闭包,sync使用细节
  • 闭包--闭包之tab栏切换(四)
  • 程序员最讨厌的9句话,你可有补充?
  • 技术发展面试
  • 解决jsp引用其他项目时出现的 cannot be resolved to a type错误
  • 如何在GitHub上创建个人博客
  • 使用 Node.js 的 nodemailer 模块发送邮件(支持 QQ、163 等、支持附件)
  • 一个完整Java Web项目背后的密码
  • [地铁译]使用SSD缓存应用数据——Moneta项目: 低成本优化的下一代EVCache ...
  • C# - 为值类型重定义相等性
  • 东超科技获得千万级Pre-A轮融资,投资方为中科创星 ...
  • 教程:使用iPhone相机和openCV来完成3D重建(第一部分) ...
  • ​七周四次课(5月9日)iptables filter表案例、iptables nat表应用
  • # Python csv、xlsx、json、二进制(MP3) 文件读写基本使用
  • $.each()与$(selector).each()
  • (14)目标检测_SSD训练代码基于pytorch搭建代码
  • (2020)Java后端开发----(面试题和笔试题)
  • (C语言)输入自定义个数的整数,打印出最大值和最小值
  • (DFS + 剪枝)【洛谷P1731】 [NOI1999] 生日蛋糕
  • (编程语言界的丐帮 C#).NET MD5 HASH 哈希 加密 与JAVA 互通
  • (附源码)ssm高校升本考试管理系统 毕业设计 201631
  • (删)Java线程同步实现一:synchronzied和wait()/notify()
  • (四)【Jmeter】 JMeter的界面布局与组件概述
  • (原創) 如何讓IE7按第二次Ctrl + Tab時,回到原來的索引標籤? (Web) (IE) (OS) (Windows)...
  • (转)EOS中账户、钱包和密钥的关系
  • .[hudsonL@cock.li].mkp勒索加密数据库完美恢复---惜分飞
  • .net 微服务 服务保护 自动重试 Polly
  • @KafkaListener注解详解(一)| 常用参数详解
  • [2009][note]构成理想导体超材料的有源THz欺骗表面等离子激元开关——
  • [AutoSar]工程中的cpuload陷阱(三)测试
  • [C# 网络编程系列]专题六:UDP编程
  • [C++]C++基础知识概述
  • [CF543A]/[CF544C]Writing Code