masktools裡的鄰域處理不僅僅有mt_luts與mt_lutsx,還有更為簡化,但是也更為常用的版本:mt_expand, mt_inpand, mt_inflate, mt_deflate。這四個函數在masktools的官方文檔裡被稱為morphologic operator,實際上本質和mt_luts(x)一樣是通過鄰域計算來實現的。
mt_expand的作用是將某像素點的值替換為鄰域內最大值,具體參數是
mt_expand(clip c, int "thY", int "thC", string "mode"),其中thY和thC限制替換過程最大變化不超過這個值,而mode是描述鄰域的字符串,實際相當於:[syntax lang="avisynth"]
mt_lutsx(c, c, c, mode="max", pixels=mode,
\ yexpr="y x - "+String(thY)+" > x "+String(thY)+" + y ?",
\ expr ="y x - "+String(thC)+" > x "+String(thC)+" + y ?")
# yexpr : max(neighbourhood) - x > thY ? x + thY : max(neighbourhood)
# uvexpr: max(neighbourhood) - x > thC ? x + thC : max(neighbourhood)[/syntax]
而mt_inpand則是將某像素點的值替換為鄰域內的最小值,參數和mt_expand相同,實際相當於:[syntax lang="avisynth"]
mt_lutsx(c, c, c, mode="min", pixels=mode,
\ yexpr="x y - "+String(thY)+" > x "+String(thY)+" - y ?",
\ expr ="x y - "+String(thC)+" > x "+String(thC)+" - y ?")
# yexpr : x - min(neighbourhood) > thY ? x - thY : min(neighbourhood)
# uvexpr: x - min(neighbourhood) > thC ? x - thC : min(neighbourhood)[/syntax]
mt_inflate的作用是計算在某像素的鄰域內的平均值,當這個值比中心像素的值高時替換中心像素,參數是mt_inflate(clip c, int "thY", int "thC"),實際相當於:[syntax lang="avisynth"]
mt_lutsx(c, c, c, mode="avg", pixels="-1 -1 -1 0 -1 1 0 -1 0 1 1 -1 1 0 1 1",
\ yexpr="y x > y x - "+String(thY)+" > x "+String(thY)+" + y ? x ?",
\ expr ="y x > y x - "+String(thC)+" > x "+String(thC)+" + y ? x ?")
# yexpr : avg(neighbourhood) > x ? avg(neighbourhood) - x > thY ? x + thY
# : avg(neighbourhood)
# : x
# uvexpr: avg(neighbourhood) > x ? avg(neighbourhood) - x > thC ? x + thC
# : avg(neighbourhood)
# : x[/syntax]
mt_deflate的作用是計算在某像素的鄰域內的平均值,當這個值比中心像素的值低時替換中心像素,參數是mt_deflate(clip c, int "thY", int "thC"),實際相當於:[syntax lang="avisynth"]
mt_lutsx(c, c, c, mode="avg", pixels="-1 -1 -1 0 -1 1 0 -1 0 1 1 -1 1 0 1 1",
\ yexpr="y x < x y - "+String(thY)+" > x "+String(thY)+" - y ? x ?",
\ expr ="y x < x y - "+String(thC)+" > x "+String(thC)+" - y ? x ?")
# yexpr : avg(neighbourhood) < x ? x - avg(neighbourhood) > thY ? x - thY
# : avg(neighbourhood)
# : x
# uvexpr: avg(neighbourhood) < x ? x - avg(neighbourhood) > thC ? x - thC
# : avg(neighbourhood)
# : x[/syntax]
注意mt_inflate/deflate與其他鄰域計算不同之處是,(目前)這兩個函數的鄰域範圍是固定的(中心像素周圍不包含自身的8個像素點,鄰域字符串的定義方式之後會講解),不能自己定義鄰域範圍。
總體來看,如果處理的是mask,mt_expand與mt_inflate是具有擴張性的,因為結果是取鄰域最高值,或高於中心像素時的均值;而mt_inpand與mt_deflate是收縮性的,因為結果是取鄰域內最低值,或低於中心像素時的均值。
YUV的計算如果考慮到鄰域,可以大大擴展應用的空間,因為鄰域處理是視頻/圖像處理裡非常重要的一個環節。我們來看一個用mt_luts配合mt_lutsx來製作一個簡單的complexity mask。畫面複雜度(complexity)是圖像處理裡非常重要的一個屬性,高複雜度的區域與低複雜度的區域往往需要不同的處理,譬如由於最後一次壓制過程導致的banding,往往只出現在畫面的低複雜度區域,而不出現在高複雜度區域,因此用一個mask來限定deband的範圍不包括高複雜度區域可以較好地保護這種區域的細節。Complexity mask的實現算法相對來說比較複雜,通常配合DCT計算AC/DC variance得到的結果比較好,而靠masktools做的話效果可能並不算理想。而且因為這個實現過程需要有較大的鄰域範圍,而masktools當鄰域範圍增大時處理速度會下降地比較厲害,而不用較大的範圍得到的結果可能不夠理想。不管怎樣,我們先來用一個試驗:[syntax lang="avisynth"]
dif_med = mt_makediff(mt_luts(last, "med", pixels=mt_circle(3), expr="y"))
c_mask = mt_lutsx(dif_med, dif_med, dif_med, "std", pixels=mt_circle(3), expr="y 2 ^")
\ .mt_expand.mt_inflate(chroma="128") # 為了後面截圖用了chroma="128",實際使用可以去掉[/syntax]
這是一個很簡單的模擬方式,先取鄰域內的中值,然後用mt_makediff來求得像素與鄰域中值之差。在複雜度較高、細節豐富的區域鄰域中值和中心像素點間的差值在鄰域內的標準差較高(好拗口…),因此衡量這個標準差可以模擬出一個複雜度mask:
源畫面
將畫面下半部分紋理較重、複雜度較高的部分mask住的效果。