相機(jī)之LUT濾鏡

相機(jī)之使用OpenGL預(yù)覽
相機(jī)之使用OpenGL拍照
相機(jī)之使用OpenGL錄像
相機(jī)之為錄像添加音頻
相機(jī)之大眼
相機(jī)之貼紙
相機(jī)之口紅
相機(jī)之美顏磨皮

LUT 濾鏡

LUT 是 Look Up Table 的簡稱,即顏色查找表钱骂,也就是輸入一個(gè)顏色茵瀑,返回另一個(gè)顏色的映射表,LUT 濾鏡實(shí)質(zhì)上就是獨(dú)立像素點(diǎn)替換,即根據(jù) OpenGL 采樣器對紋理進(jìn)行采樣得到的像素點(diǎn)勺阐,再基于像素點(diǎn)的(R绊谭,G,B)分量查表氓涣,獲得 LUT 映射的(R1牛哺,G1,B1)劳吠,替換原來的輸出

RGB 的每個(gè)顏色分量均有 256 中可能引润,因此 RGB 完整的顏色就有 256 * 256 * 256 種顏色,而每個(gè)顏色占用 3 個(gè)字節(jié)痒玩,也就有 256 * 256 * 256 * 3 個(gè)字節(jié)淳附,即 48M 內(nèi)存空間

為了降低內(nèi)存占用,LUT 以一定的采樣間隔凰荚,將相近的 n (采樣步長燃观,通常為 4 ) 種顏色采樣使用一條映射記錄并存儲(chǔ),這樣只需要 64 * 64 * 64 * 3 個(gè)字節(jié)便瑟,對于忽略的顏色值可以進(jìn)行插值獲得其相似結(jié)果

LUT 通常是用一張分辨率為 512 X 512 的圖片


Look Up Table.png

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

LUT濾鏡優(yōu)缺點(diǎn)

LUT可以應(yīng)用于Camera實(shí)時(shí)預(yù)覽和視頻實(shí)時(shí)處理,速度快屿讽,效果穩(wěn)定昭灵,更容易進(jìn)行資源化配置,由于LUT是以圖像資源的方式存在伐谈,隨著LUT數(shù)量的增多烂完,會(huì)增加軟件包體積的大小,主流的優(yōu)化方式是將資源線上化

片段著色器

precision mediump float;

uniform sampler2D vTexture;
uniform sampler2D s_LutTexture;
varying vec2 v_TextureCoord;
uniform float filterRatio;

void main(){
    // 首先原始采樣像素的 RGBA 值
    vec4 textureColor =texture2D(vTexture, v_TextureCoord);
    // textureColor.b是一個(gè)0-1之間的浮點(diǎn)數(shù)诵棵,乘以63確定B分量所在位置(0-63)
    float blueColor = textureColor.b * 63.0;

    // 因?yàn)闀?huì)出現(xiàn)浮點(diǎn)誤差抠蚣,所以取與B分量值最接近的2個(gè)小方格的坐標(biāo),之后根據(jù)小數(shù)點(diǎn)進(jìn)行插值運(yùn)算
    vec2 quad1;
    // floor 向下取整
    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);

    // 計(jì)算R分量和G分量
    vec2 texPos1;
    // 每個(gè)小格子代表的紋理坐標(biāo)長度是1/8=0.125
    // ((0.125-1.0/512.0)*textureColor.r) 也就是 ((64-1)*textureColor.r)/512
    // (64-1)*textureColor.r 將當(dāng)前實(shí)際像素的r值映射到0-63履澳,除以512是轉(zhuǎn)化為紋理坐標(biāo)中實(shí)際的點(diǎn)嘶窄,LUT圖的分辨率為512*512
    //  0.5/512.0 表示每個(gè)小格子中怀跛,R分再為64個(gè),G分再為64個(gè)柄冲,形成的最小的小方格的一半
    texPos1.x = (quad1.x * 0.125) + ((0.125 - 1.0/512.0) * textureColor.r) + 0.5/512.0;
    texPos1.y = (quad1.y * 0.125) + ((0.125 - 1.0/512.0) * textureColor.g) + 0.5/512.0;
    vec2 texPos2;
    texPos2.x = (quad2.x * 0.125) + ((0.125 - 1.0/512.0) * textureColor.r) + 0.5/512.0;
    texPos2.y = (quad2.y * 0.125) + ((0.125 - 1.0/512.0) * textureColor.g) + 0.5/512.0;

    // 取目標(biāo)映射對應(yīng)的像素值
    vec4 newColor1 = texture2D(s_LutTexture, texPos1);
    vec4 newColor2 = texture2D(s_LutTexture, texPos2);

    // 使用mix方法對2個(gè)邊界像素值進(jìn)行插值混合吻谋,fract函數(shù)是指取小數(shù)部分
    vec4 newColor = mix(newColor1, newColor2, fract(blueColor));

    // 將原圖與映射后的圖進(jìn)行插值混合,adjust調(diào)節(jié)范圍為(0-1)羊初,取0時(shí)完全使用原圖滨溉,取1時(shí)完全取映射后的濾鏡圖
    gl_FragColor = mix(textureColor, vec4(newColor.rgb, textureColor.w), filterRatio);
}

著色器程序

class LutFilter(context: Context, width: Int, height: Int) :
    FboFilter(context, R.raw.base_vertex, R.raw.lut_frag, width, height) {

    var filterRatio = 0.0f
    private val lutTextureId = TextureUtil.loadTexture(context, R.drawable.lut)
    private val s_LutTexture = GLES20.glGetUniformLocation(mProgram, "s_LutTexture")
    private val filterRatioLocation = GLES20.glGetUniformLocation(mProgram, "filterRatio")
    
    override fun onDrawInFBO(textureId: Int) {

        // 先將textureId的圖像畫到這一個(gè)FBO中
        //激活紋理單元0
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
        //將textureId紋理綁定到紋理單元0
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId)
        //將紋理單元0傳給vTexture,告訴vTexture采樣器從紋理單元0讀取數(shù)據(jù)
        GLES20.glUniform1i(vTexture, 0)

        // 傳遞LUT圖的采樣器
        GLES20.glActiveTexture(GLES20.GL_TEXTURE1)
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, lutTextureId)
        GLES20.glUniform1i(s_LutTexture, 1)
        GLES20.glUniform1f(filterRatioLocation, filterRatio)

        //在textureId紋理上畫出圖像
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4)

        //解除綁定
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0)
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末长赞,一起剝皮案震驚了整個(gè)濱河市晦攒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌得哆,老刑警劉巖脯颜,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異贩据,居然都是意外死亡栋操,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進(jìn)店門饱亮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來矾芙,“玉大人,你說我怎么就攤上這事近上√尴埽” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵壹无,是天一觀的道長葱绒。 經(jīng)常有香客問我,道長斗锭,這世上最難降的妖魔是什么地淀? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮岖是,結(jié)果婚禮上帮毁,老公的妹妹穿的比我還像新娘。我一直安慰自己豺撑,他們只是感情好作箍,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著前硫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪荧止。 梳的紋絲不亂的頭發(fā)上屹电,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天阶剑,我揣著相機(jī)與錄音,去河邊找鬼危号。 笑死牧愁,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的外莲。 我是一名探鬼主播猪半,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼偷线!你這毒婦竟也來了磨确?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤声邦,失蹤者是張志新(化名)和其女友劉穎乏奥,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體亥曹,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡邓了,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了媳瞪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片骗炉。...
    茶點(diǎn)故事閱讀 40,561評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蛇受,靈堂內(nèi)的尸體忽然破棺而出句葵,到底是詐尸還是另有隱情,我是刑警寧澤龙巨,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布笼呆,位于F島的核電站,受9級特大地震影響旨别,放射性物質(zhì)發(fā)生泄漏诗赌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一秸弛、第九天 我趴在偏房一處隱蔽的房頂上張望铭若。 院中可真熱鬧,春花似錦递览、人聲如沸叼屠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽镜雨。三九已至,卻和暖如春儿捧,著一層夾襖步出監(jiān)牢的瞬間荚坞,已是汗流浹背挑宠。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留颓影,地道東北人各淀。 一個(gè)月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像诡挂,于是被迫代替她去往敵國和親碎浇。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評論 2 359