圖像處理之LUT

目錄

  • LUT原理

  • LUT濾鏡特效Shader解讀

演示demo

前言:

平常我們使用的圖片編輯app真竖,一般都有這個最基本的LUT濾鏡,都是UI設(shè)計師設(shè)計好風(fēng)格后吵护,導(dǎo)出LUT效果圖焦读,有了這個LUT效果圖,在app端睁冬,借助GPUImage庫,便可以方便的實現(xiàn)這種特效看疙。

甚至有些開發(fā)者豆拨,是直接破解別人的app直奋,拿到里面的LUT圖,不需要自己專門設(shè)計施禾,便能輕易的竊取到別人app里這些好看的風(fēng)格的濾鏡

LUT在圖像處理中的位置

先從大方向上看LUT在數(shù)字圖像處理上處于什么位置

圖像處理大致可以分為以下幾類:

  • 獨立像素點運算

包括亮度脚线、對比、飽和度弥搞、色調(diào)邮绿、灰色化等

  • 多個像素點運算

一般是進行卷積變換,求均值攀例,求中值斯碌,插值等,包括邊緣檢測肛度、浮雕化傻唾、模糊、銳化

  • 幾何變化

矩陣變換承耿。包括縮放冠骄、旋轉(zhuǎn)、傾斜加袋、扭曲凛辣、液化等

  • 多圖像合成

多張圖像的處理,包括添加水印职烧,貼紙扁誓,美妝等。

LUT歸為獨立像素點運算這種圖像處理

LUT介紹

LUT 是 LookUpTable 的簡稱蚀之,也稱作顏色查找表

從名字上看蝗敢,我們大概可以知道,是用來查找顏色的足删,他確實就是這樣的寿谴,是通過一種顏色,查找其映射后的顏色失受,可以理解為一個函數(shù)LUT(R1,G1,B1),帶R,G,B三個自變量的函數(shù)讶泰,輸出為其對應(yīng)映射后的值R2,G2,B2

LUT(R1, G1, B1) = (R2, G2, B2)

對于RGB顏色空間來說,顏色是由RGB三種基色構(gòu)成拂到,所有像素點的顏色值痪署,都可以通過RGB組合而成,我們量化顏色時兄旬,一般使用8bit來表示RGB中的一個分量狼犯,所以每一個分量可以表示為2的8次方,即0~255。

那么要建立一個顏色映射表辜王,我們就可以使用255* 255* 255種來表示所有顏色映射后的值

這是非常精準(zhǔn)的,每一種顏色都可以查找到

當(dāng)我們需要把一幅原始圖像的顏色風(fēng)格轉(zhuǎn)換為一種懷舊風(fēng)格時罐孝,

上面的這個需求呐馆,我們完全可以搞一個三維數(shù)組,數(shù)組的大小256 * 256 * 256莲兢,數(shù)組存儲所有懷舊風(fēng)格顏色映射后的值汹来,直接通過查表,就能轉(zhuǎn)換風(fēng)格了改艇,確實可以的

但是上面的這種實現(xiàn)存在一些問題:

  • 1.數(shù)字太難管理收班,難于保存,難于復(fù)用谒兄,容易出錯摔桦,一不小心修改一個數(shù)字,因為什么原因丟失承疲,弄錯邻耕,效果就變了,定位錯誤非常困難

  • 2.顏色查找表格過大

而LUT能很好的解決上面的問題燕鸽。

首先解決第一個難以管理兄世,容易出錯,出錯難以排查的問題啊研。

如果我們使用一張圖存儲上述信息御滩,那么就不會容易出錯了,圖保存和復(fù)用非常方便党远,也不容易出錯削解,圖沒錯,信息就不會有錯誤沟娱。

沒有找到 256 * 256 * 256 的LUT圖(即16 * 16個方格的LUT圖钠绍,每個方格里有16 * 16個小格),因為沒有這種設(shè)計花沉,所以找不到柳爽,但是完全可以使用64 * 64 * 64的圖,代替256 * 256 * 256的表格碱屁,來保存數(shù)據(jù)磷脯,這樣圖代替表格完成數(shù)據(jù)保存,就解決了表格數(shù)據(jù)容易出錯娩脾,出錯后排查困難的問題赵誓。

我們現(xiàn)在使用64 * 64 * 64顆粒度的LUT圖保存數(shù)據(jù),即將256歸化到64而已。

那怎么用一張圖來代替數(shù)據(jù)表來保存信息呢俩功,這是一個巧妙的設(shè)計幻枉。

下面是一個LUT效果圖

image

LUT就是用圖代替了表,完美的解決了上面表存在的問題

我們來看下诡蜓,LUT如何能代替顏色查找表的功能

比如想查找純藍色色(0熬甫,0,1)對應(yīng)的映射值

LUT(R1, G1, B1) = (?, ?, ?)

我們先看這個顏色查找表的特征:

粗看的話蔓罚,我們可以得出一個結(jié)論:
8 * 8個方格椿肩,總體上,底部越來越藍豺谈;而對于每一個方格郑象,則越往右越紅,越往下越綠茬末;

哈哈厂榛,沒錯,我猜你會關(guān)聯(lián)到了我們的RGB顏色了丽惭。

這是一個64 * 64 * 64顆粒度的LUT設(shè)計噪沙,總的方格大小為512 * 512, 8 * 8 64個方格吐根,所以每個方格大小為64 * 64正歼。

64個方格,每個方格大小為 64 * 64 拷橘, 所以叫做64 * 64 * 64顆粒度的設(shè)計局义。因為顏色值的范圍為0~255,即256個取值冗疮,將256個取值歸化到64萄唇。

從左上到右下(可以想作z方向),越來越藍术幔,藍色值B從0~255另萤,代表用來查找的B,即LUT(R1,G1,B1) = (R2,G2,B2)中的B1诅挑,

每一個方格里四敞,從左往右(x方向),紅色值R從0~255拔妥,代表用來查找的R忿危,即LUT(R1,G1,B1) = (R2,G2,B2)中的R1;

每一個方格里没龙,從上往下(y方向)铺厨,綠色值G從0~255缎玫,代表用來查找的G,即LUT(R1,G1,B1) = (R2,G2,B2)中的G1解滓;

因為一個顏色分量是0~255赃磨,所以一個方格表示的藍色范圍為4,比如最左上的方格藍色為0~4洼裤,查找時邻辉,如果有某個像素的藍色值在0~4之間,則一定是在第一個方格里查找其映射后的顏色

通過顏色找位置逸邦,找到的位置對應(yīng)的點的顏色即是這個顏色映射后的顏色,如下圖的五角星??的顏色既是某個顏色映射后的顏色

image

這樣在扰,我們就完成了使用圖代替表格缕减,進行數(shù)據(jù)的存儲,并且對圖的數(shù)據(jù)存儲進行了4 * 4 * 4倍的壓縮處理

有了這些了解后芒珠,我們來嘗試查找像素點歸一化后的純藍色(0桥狡,0,1)的映射后的顏色皱卓。

需求變?yōu)椋?/p>

我們是想通過顏色S裹芝,從LUT圖上,查找其映射后的顏色T

思路是娜汁,通過顏色S嫂易,找到其在LUT上的位置,位置上對應(yīng)的顏色即是要找的顏色T

  • 1.使用藍色B定位方格n

n =  1(藍色值) * 63(一共64個方格掐禁,從第0個算起) = 63

故要定位的方格n是第63個

  • 2.定位在方格里的位置怜械,使用R,G定位位置x,y
x = 0(R值) * 63(每個方格大小為 64 * 64) = 0, y = 0(G值) * 63(每個方格大小為 64 * 64) = 0

所以方格的(0,0)位置為要定位的x傅事,y

  • 3.定位在整個圖中位置

在512 * 512的大小上缕允,其坐標(biāo)為:

Py = floor(n/8) * 64 + y = 7 * 64 + 0 = 448;
Px = [n - floor(n/8) * 8] * 64 + x = [63 - 7*8] * 64 + 0 = 448;
P = (448, 448)


其中 floor(n/8)代表位置所在行蹭越,每一行的長度為64障本,y為方格里的G定位的位置;
[n - floor(n/8) * 8]代表位置所在列數(shù)响鹃,每一列的長度為64驾霜,x為方格里的R定位的位置;
floor為向下取整买置,ceil為向上取整寄悯。比如2.3, floor(2.3) = 2; ceil(2.3) = 3;

方格大小為512 * 512, 位置為P = (448, 448), 歸一化后為(7/8堕义, 7/8)猜旬,很明顯脆栋,顏色值(0,0洒擦,1)的位置確實在第63個方格的左上角

4.計算映射后顏色
// 這里使用GPU采樣器對紋理采樣
 vec4 newColor = texture(sample, texPos);
 
 其中texPos就是第三步的歸一化后的P

現(xiàn)在我們已經(jīng)完成了通過LUT查找顏色的映射值椿争,理解了這個之后,我們后面用代碼來實現(xiàn)熟嫩。

現(xiàn)在秦踪,再回到我們說LUT能夠解決前面的顏色查找表存在的問題

1.數(shù)據(jù)大小:這里使用的是 512 * 512大小的尺寸的LUT圖掸茅,比前面的顏色查找表要小

2.圖片很方便存儲椅邓,移植性非常好,做好一次后昧狮,非常方便重復(fù)使用

其他的優(yōu)點:

  • 所有的算法計算景馁,都是對LUT進行計算位置,取位置處的顏色值逗鸣,有助于算法本身的保護

  • LUT的設(shè)計更容易進行熱更新合住,設(shè)計師設(shè)計好效果可以動態(tài)發(fā)布

缺點:

  • 1.LUT資源容易被破解、泄密(得到LUT圖撒璧,就相當(dāng)于得到一個算子)
  • 2.尺寸512 * 512 還是比較大透葛,增加軟件包的體積(可以考慮后下發(fā)來優(yōu)化)

思考:

對于缺點2,LUT圖大小一定要使用512 * 512嗎卿樱?是否可以再縮小呢僚害?

我們前面論述的時候,已經(jīng)將256 * 256 * 256 壓縮到了 64 * 64 * 64繁调,完全可以再壓縮贡珊。

實際上,我們一般設(shè)計為方形的涉馁,算法也簡化门岔,對于R,G烤送,B系數(shù)是一樣的處理寒随,我們可以將256歸化為64,還可以繼續(xù)歸化為16帮坚。 即從256 * 256 * 256變?yōu)?6 * 16 * 16妻往,其對應(yīng)的LUT如下:

image

這個公司的一個項目里是有使用的

補充

如何查找顏色A(0.4,0.6试和,0.2)映射的顏色呢讯泣?

  • 1.使用藍色B定位方格n
n = b * 63 = 0.2 * 63 = 12.6

要定位的方格n是第12.6個,是個小數(shù)阅悍,那到底是用第12個好渠,還是用第13個呢昨稼?我們采用兩個,對兩個方格的取色結(jié)果進行混合即可,首先使用第12個方格計算拳锚,然后再使用第13個方格計算假栓,最后混合兩個方格的顏色

  • 2.在方格里,使用R霍掺,G定位位置x,y
x = 0.4 * 63 = 25.2, y = 0.6 * 63 = 37.8

  • 3.定位兩個方格對應(yīng)的位置

在512 * 512的大小上匾荆,計算其坐標(biāo):

// 先使用第12個方格定位其坐標(biāo)P1
Py = floor(n/8) * 64 + y = 1 * 64 + 37.8 = 101.8;
Px = [n - floor(n/8) * 8] * 64 + x = [12 - 1*8] * 64 + 25.2 = 281.2;
P1 = (Px, Py)=(281.2杆烁, 111.8);

歸一化后為P1 = (281.2牙丽, 111.8)/512 = (0.549, 0.2184);

// 先使用第13個方格定位其坐標(biāo)P2
Py = floor(n/8) * 64 + y = 1 * 64 + 37.8 = 101.8;
Px = [n - floor(n/8) * 8] * 64 + x = [13 - 1*8] * 64 + 25.2 = 345.2;
P2 = (Px, Py)=(345.2, 111.8);

歸一化后為P2 = (345.2, 111.8)/512 = (0.674, 0.2184);

  • 4.計算顏色
// 這里使用GPU采樣器對紋理采樣
 vec4 newColor1 = texture(sample, texPos1);
 vec4 newColor2 = texture(sample, texPos2);
 
 其中texPos1就是第三步的P1兔魂, texPos2是第三步的P2
  • 5.混合顏色

resColor = mix(newColor1, newColor2, a);

a =   fract(blueColor);

blueColor 為12.6烤芦,小數(shù)部分越大,越接近13入热,所以第13個方格占比越大

ps:
a = fract(blueColor); //fract(x) 獲取x的小數(shù)部分
mix(x, y, a); //取x,y的線性混合,x(1-a)+ya

了解了這個后拍棕,再來看LUT濾鏡里glsl代碼的shader算法部分晓铆,就很容易了

GLSL的LUT濾鏡shader解讀

fragment half4 lookupFragment(TwoInputVertexIO fragmentInput [[stage_in]],
                              texture2d<half> inputTexture [[texture(0)]],
                              texture2d<half> inputTexture2 [[texture(1)]],
                              constant IntensityUniform& uniform [[ buffer(0) ]])
{
    constexpr sampler quadSampler;
    half4 base = inputTexture.sample(quadSampler, fragmentInput.textureCoordinate);
    
    // 獲取藍色
    half blueColor = base.b * 63.0h;
    
    // 通過藍色計算兩個方格quad1勺良,quad2
    half2 quad1;
    quad1.y = floor(floor(blueColor) / 8.0h); 
    quad1.x = floor(blueColor) - (quad1.y * 8.0h);
    
    half2 quad2;
    quad2.y = floor(ceil(blueColor) / 8.0h); //ceil 向下取整,ceil(12.6) = 13, 解決跨行時計算問題骄噪,比如blueColor = 7.6尚困,則取第7,8個方格链蕊,他們不在同一行
    quad2.x = ceil(blueColor) - (quad2.y * 8.0h);
    
    // 計算映射后顏色所在兩個方格的位置的歸一化紋理坐標(biāo)
    float2 texPos1;
    texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * base.r);
    texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * base.g);
    
    float2 texPos2;
    texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * base.r);
    texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * base.g);
    
    // 取出對應(yīng)坐標(biāo)的顏色newColor1事甜,newColor2
    constexpr sampler quadSampler3;
    half4 newColor1 = inputTexture2.sample(quadSampler3, texPos1);
    constexpr sampler quadSampler4;
    half4 newColor2 = inputTexture2.sample(quadSampler4, texPos2);
    
    // 混合顏色newColor1,newColor2滔韵,得到查找的顏色color_t
    half4 newColor = mix(newColor1, newColor2, fract(blueColor));
    
    // 調(diào)節(jié)強度時逻谦,將color_t和源色color_s進行混合
    return half4(mix(base, half4(newColor.rgb, base.w), half(uniform.intensity)));
}

  • 1.通過B分量確定兩個方格
    half2 quad1;
    quad1.y = floor(floor(blueColor) / 8.0h); 
    quad1.x = floor(blueColor) - (quad1.y * 8.0h);
    
    half2 quad2;
    quad2.y = floor(ceil(blueColor) / 8.0h); 
    quad2.x = ceil(blueColor) - (quad2.y * 8.0h);
    
   
    比如 base(0.4,0.6陪蜻,0.2), 先確定第一個方格:
    
    base.b = 0.2邦马,blueColor = 0.2 * 63 = 12.6,(即為第12個,第13個方格)宴卖,但是我們要計算它坐在行和列滋将, floor(12.6) = 12,  floor(12 / 8.0h) = 1,即第一行;
    floor(blueColor) - (quad1.y * 8.0h) = floor(12.6) - (1 * 8) = 4症昏,即第4列随闽;
    
    同理可以算出第二個方格為第1行,第5列
    
    //ceil 向下取整肝谭,ceil(12.6) = 13, 解決跨行時計算問題掘宪,比如blueColor = 7.6蛾扇,則取第7,8個方格添诉,他們不在同一行
  • 2.確定方格映射后的坐標(biāo)
    float2 texPos1;
    texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * base.r);
    texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * base.g);
    
    float2 texPos2;
    texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * base.r);
    texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * base.g);
    
   其中 (quad1.x * 0.125) 表示行歸一化的坐標(biāo)屁桑,(quad1.y * 0.125)表示列歸一化的坐標(biāo),一共8行栏赴,每一行的長度為1/8 = 0.125蘑斧,一共8列,每一列的長度為1/8 = 0.125须眷;
   
   0.125 * base.r表示一個方格里紅色的位置竖瘾,因為一個方格長度為0.125,r從0~1花颗;綠色同理捕传;
   
   需要留意的是這里有個0.5/512, 和 1.0/512.
   
   0.5/512 是為了取點的中間值扩劝,一個點長度為1庸论,總長度512,取點的中間值棒呛,即為0.5/512聂示;
   
   1.0/512, 是因為計算texPos2.x時簇秒,單獨對于一個方格來說鱼喉,是從0~63,所以為63/512趋观,即0.125 - 1.0/512扛禽;
    
  • 3.取出兩個方格里對應(yīng)坐標(biāo)的顏色newColor1,newColor2
    constexpr sampler quadSampler3;
    half4 newColor1 = inputTexture2.sample(quadSampler3, texPos1);
    constexpr sampler quadSampler4;
    half4 newColor2 = inputTexture2.sample(quadSampler4, texPos2);
  • 4.混合顏色newColor1皱坛,newColor2编曼,得到查找的顏色color_t
    // 混合顏色newColor1,newColor2剩辟,得到查找的顏色color_t
    half4 newColor = mix(newColor1, newColor2, fract(blueColor));

至此掐场,LUT特效濾鏡的實現(xiàn)原理,我們就明白了抹沪。

參考:

落影大神:http://www.reibang.com/p/96a61110a5ae

其他:http://www.reibang.com/p/f39f051595bb

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末刻肄,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子融欧,更是在濱河造成了極大的恐慌敏弃,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件噪馏,死亡現(xiàn)場離奇詭異麦到,居然都是意外死亡绿饵,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門瓶颠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拟赊,“玉大人,你說我怎么就攤上這事粹淋∥睿” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵桃移,是天一觀的道長屋匕。 經(jīng)常有香客問我,道長借杰,這世上最難降的妖魔是什么过吻? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮蔗衡,結(jié)果婚禮上纤虽,老公的妹妹穿的比我還像新娘。我一直安慰自己绞惦,他們只是感情好逼纸,可當(dāng)我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著翩隧,像睡著了一般樊展。 火紅的嫁衣襯著肌膚如雪呻纹。 梳的紋絲不亂的頭發(fā)上堆生,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機與錄音雷酪,去河邊找鬼淑仆。 笑死,一個胖子當(dāng)著我的面吹牛哥力,可吹牛的內(nèi)容都是我干的蔗怠。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼吩跋,長吁一口氣:“原來是場噩夢啊……” “哼寞射!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起锌钮,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤桥温,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后梁丘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體侵浸,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡旺韭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了掏觉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片区端。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖澳腹,靈堂內(nèi)的尸體忽然破棺而出织盼,到底是詐尸還是另有隱情,我是刑警寧澤酱塔,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布悔政,位于F島的核電站,受9級特大地震影響延旧,放射性物質(zhì)發(fā)生泄漏谋国。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一迁沫、第九天 我趴在偏房一處隱蔽的房頂上張望芦瘾。 院中可真熱鬧,春花似錦集畅、人聲如沸近弟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽祷愉。三九已至,卻和暖如春赦颇,著一層夾襖步出監(jiān)牢的瞬間二鳄,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工媒怯, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留订讼,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓扇苞,卻偏偏與公主長得像欺殿,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子鳖敷,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,792評論 2 345