OpenGL ES 相機(jī) LUT 濾鏡
OpenGLES 相機(jī) LUT 濾鏡

左側(cè)為 LUT 濾鏡效果,右側(cè)為原圖
什么是 LUT ? LUT 是 Look Up Table 的簡稱,稱作顏色查找表,是一種針對色彩空間的管理和轉(zhuǎn)換技術(shù)。
它可以分為一維 LUT(1D LUT) 和 三維 LUT(3D LUT),其中三維 LUT 比較常用。簡單來講,LUT 就是一個 RGB 組合到另一個 RGB 組合的映射關(guān)系表。
LUT(R, G, B) = (R1, G1, B1)
LUT 濾鏡是一種比較經(jīng)典的濾鏡,本質(zhì)上屬于獨(dú)立像素點(diǎn)替換,即根據(jù) OpenGL 采樣器對紋理進(jìn)行采樣得到的像素點(diǎn),再基于像素點(diǎn)的(R,G,B)分量查表,獲得 LUT 映射的(R1,G1,B1),替換原來的輸出。
一般 RGB 像素占用 3 個字節(jié),包含 3 個分量,每個分量有 256 種取值,那么三維 LUT 模板就可以包含 256 X 256 X 256 種情況,占用 48MB 內(nèi)存空間。
這樣一個 LUT 模板內(nèi)存占用過大同時也降低了查找的效率,通常會采取下采樣方式來降低數(shù)據(jù)量。
例如可以對三維 LUT 模板每個分量分別進(jìn)行 64 次采樣,這樣就獲得一個 64 X 64 X 64 大小的映射關(guān)系表,對于不在表內(nèi)的顏色值可以進(jìn)行插值獲得其相似結(jié)果。
三維 LUT 模板,即64 X 64 X 64 大小的映射關(guān)系表,通常是用一張分辨率為 512 X 512 的二維圖片表示,稱為 LUT 圖(模板圖)。

LUT 圖在橫豎方向上被分成了 8 X 8 一共 64 個小方格,每一個小方格內(nèi)的 B(Blue)分量為一個定值,64 個小方格一共表示了 B 分量的 64 種取值。
對于每一個小方格,橫豎方向又各自分為 64 個小格,以左下角為原點(diǎn),橫向小格的 R(Red)分量依次增加,縱向小格的 G(Green)分量依次增加。

至此,我們可以根據(jù)原始采樣像素 RGB 中的 B 分量值,確定我們要選用 LUT 圖中的第幾個小格,然后再根據(jù)(R,G)分量值為縱橫坐標(biāo),確定映射的 RGB 組合。
OpenGLES 實(shí)現(xiàn) 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, 取值范圍轉(zhuǎn)為 0~63
float blueColor = textureColor.b * 63.0;
//取與 B 分量值最接近的 2 個小方格的坐標(biāo)
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 分量的值確定小方格內(nèi)目標(biāo)映射的 RGB 組合的坐標(biāo),然后歸一化,轉(zhuǎn)化為紋理坐標(biāo)。
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);
//取目標(biāo)映射對應(yīng)的像素值
vec4 newColor1 = texture2D(s_LutTexture, texPos1);
vec4 newColor2 = texture2D(s_LutTexture, texPos2);
//使用 Mix 方法對 2 個邊界像素值進(jìn)行混合
vec4 newColor = mix(newColor1, newColor2, fract(blueColor));
gl_FragColor = mix(textureColor, vec4(newColor.rgb, textureColor.w), 1.0);
}
LUT 濾鏡對比圖

-- END --
技術(shù)交流掃碼添加我的微信:Byte-Flow
免費(fèi)獲取視頻教程和源碼
推薦:
FFmpeg + OpenGLES 實(shí)現(xiàn)視頻解碼播放和視頻濾鏡
Android OpenGL ES 從入門到精通系統(tǒng)性學(xué)習(xí)教程
覺得不錯,點(diǎn)個在看唄~

