分页: 2 / 6

10. mask處理函數——hysteresis、logic、invert、binarize

发表于 : 2012-06-22 7:16
06_taro
首先來看mt_hysteresis(small_mask, big_mask)。這個函數是取一個small mask與一個big mask,對於一整塊在big mask裡連通的區域,看small mask裡是否有點位於這個區域內,如果有的話就保留這個區域,如果沒有的話,認為這個區域的big mask是無效的(可能因為噪點等各種原因誤判的部分)。讓我們看一個實例:[syntax lang="avisynth"]
small = mt_edge("roberts", chroma="128")
big = mt_edge("hprewitt", chroma="128")
mt_hysteresis(small, big, chroma="128")[/syntax]
這裡我們希望邊緣既有hprewitt的覆蓋面,又能像roberts那樣盡量減小噪點的干擾,於是對於roberts檢測裡不包含的部分直接從hprewitt中去除,從而得到一個robustness更高的廣域邊緣部分。效果如圖示:
图片

图片

图片

接下來是mt_logic(clip1, clip2, string "mode", int "th1", int "th2")。這是一個對clip1與clip2進行邏輯運算的函數。它支持6個mode,分別是:
mode
= "and",按位與運算,例: 11 & 5 = 1011 & 101 = 1
= "or",按位或運算,例: 11 | 5 = 1011 | 101 = 1111 = 15
= "xor",按位異或運算,例: 11 ^ 5 = 1011 ^ 101 = 1110 = 14
= "andn",按位與非運算,例: 11 & ~5 = 1011 & ~101 = 1011 & 11111010 = 1010 = 10
= "min",取最小值,即 min(clip1+th1, clip2+th2)
= "max",取最大值,即 max(clip1+th1, clip2+th2)
例如前面的[syntax lang="avisynth"]
temporal_bright = src.mt_lutxy(back_compensate, "x y max").mt_lutxy(forw_compensate, "x y max")
temporal_dark = src.mt_lutxy(back_compensate, "x y min").mt_lutxy(forw_compensate, "x y min")[/syntax]
就相當於[syntax lang="avisynth"]
temporal_bright = src.mt_logic(back_compensate, "max").mt_logic(forw_compensate, "max")
temporal_dark = src.mt_logic(back_compensate, "min").mt_logic(forw_compensate, "min")[/syntax]
如果我們想做一個luma自適應的deband,按照luma高時使用src,luma低時使用debanded,同時用edge mask來保護edge的話,可以用mt_logic來組合這個mask:[syntax lang="avisynth"]
edge_mask = src.mt_edge(thY2=255).mt_lut("x 1.2 ^")
luma_mask = src.RemoveGrain(20, -1).mt_lut("x 16 - 255 * 219 /") # 擴張到PC range以填滿0-255
final_mask = mt_logic(edge_mask, luma_mask, "max")
mt_merge(f3kdb, src, final_mask, luma=true)[/syntax]
這裡的f_mask就包含了edge mask與luma mask的最大值,從而對不需要做deband的亮區與線條都做了保護。

mt_invert,這是一個非常簡單的函數,相當於mt_lut("255 x -"),即每個像素直接取255-x,得到一個與原始畫面色彩顛倒的結果。如果用在mask上,就是將mask黑白顛倒過來,例如
mt_merge(a, b, mask, U=3, V=3)就相當於mt_merge(b, a, mask.mt_invert, U=3, V=3)

mt_binarize(clip c, int "threshold", bool "upper", string "mode")
直接將值域為[0, 255]的mask轉為二值mask的函數。例如前面的mt_edge(thY1=10, thY2=10, thC1=10, thC2=10, U=3, V=3)就相當於mt_edge(thY1=0, thY2=255, thC1=0, thC2=255, U=3, V=3).mt_binarize(threshold=10, U=3, V=3),將高於10的像素全部變成255,而低於10的像素全部變成0

11. convolution及其他mask處理技巧

发表于 : 2012-06-22 7:17
06_taro
mt_convolution(clip c, string "horizontal", string "vertical", bool "saturate", float "total")是一個convolution filter,你可以用mt_convolution("1 6 1", "1 6 1")進行一次簡單的blur,也可以用mt_convolution("1 -6 1", "1 -6 1")進行一次低質量的sharpen。當然用作一般用途這個函數並不合適,但是如果是要對mask或者diff進行一些粗糙的處理的話,這是一個較為快速的方法。convolution是對鄰域內像素按照kernel做一次線性組合當作最終的像素值,具體理論可以按查閱圖像處理裡convolution的相關資料。

mt_mappedblur(clip c, clip map, string "kernel", string "mode")是另一個convolution用的函數。與mt_convolution不同的是,它將horizontal與vertical兩個kernel參數合併為一個kernel,例如用kernel="1 2 1 2 8 2 1 2 1"進行一次簡單的blur。另外,mt_mappedblur需要一個map clip,在convolution過程中,如果像素變化超過map clip的話,則使用中心像素替代它的值(mode="replace"時)/完全不考慮這個像素(mode="dump"時)。這種機制比較特殊,雖然並非沒有用,不過我個人還很少見到有實際使用這種方式進行convolution的…

在實際mask的處理當中,雖然對mask進行一定的blur增強其穩定性非常常見,不過我們通常很少用mt_convolution或者mt_mappedblur來完成這個任務。相比之下,RemoveGrain與mt_lut更加常見。RemoveGrain有著非常快的處理速度,而且它內部包含了大量的3x3的kernel,可以適應大部分我們需要的處理。而mt_lut則常常用於對mask進行一些數值上的處理,例如用mt_lut("x 32 > x 1.4 ^ 0 ?")這種方式將mask中值在32以內的點都去除,以降低mask中不穩定的部分,而對值在32以上的有效部分做一次1.4次方的處理(最低的32被提升到128,而53或以上的值被提升到255)。

多個mask過程常常可以合併,以減少重複計算,提高整體速度。例如對一個視頻我們需要對線條做anti-aliasing,需要對線條邊緣做dehalo,需要對非線條的暗場進行deband,而非線條的亮場略微進行一次spatial denoise,對非動態部分進行一次temporal stabilize,同時還想對動態部分被motion blur之後的線條稍微做一點銳化,我們只需要edge、luma與motion三個mask為核心做這樣的處理:[syntax lang="avisynth"]
m_edge = mt_edge("sobel", 7, 20).mt_inflate
zero_l = 64 # 最容易出banding的其實不是luma=0的暗場,而是Y在64附近的區域,因此將64設定為luma mask裡的0值,向上下遞增
m_luma = mt_lut("x "+String(zero_l)+" - Abs 255 * "+String(zero_l)+" 128 - Abs 128 + /")
# Abs(x-zero_l)*255 / (Abs(zero_l-128)+128)
m_motn = mt_motion

m_aa = m_edge.mt_expand
m_halo = m_aa.mt_expand.mt_lutxy(m_edge, "x y -")
m_fin = mt_logic(m_edge, m_motn, "max")

aa_clp = AAA
dh_clp = aa_clp.Dehalo_alpha
db_clp = f3kdb
nr_clp = dfttest(tbsize=1)

flat = mt_merge(db_clp, nr_clp, m_luma, luma=true)
non_hl = mt_merge(flat, aa_clp, m_aa)
spat = mt_merge(non_hl, dh_clp, m_halo)

stab = spat.TTempSmoothF
sharp = spat.Sharpen
temp = mt_merge(stab, sharp, m_fin)[/syntax]
這樣一個edge mask被使用了多次,一個luma mask和motion mask也分別用於兩個處理,大大減少了對每一個處理單獨mask的開銷。

mask的處理中,mt_expand/mt_inpand/mt_inflate/mt_deflate是非常重要的函數,根據鄰域像素進行的擴張/收縮對於一個未處理的核心mask作用範圍的控制最主要就是依靠這四個函數來快速處理,一般來說只有在比較特殊的情況下我們才會考慮使用複雜的mt_luts(x)。

mask的處理根據需求不同,有大量不同的用法,在應用中才能積累出大量經驗。在這裡我就不多講經驗性的東西了,希望各位能在自己的使用中多多體會。

12. 輔助函數——鄰域字符串生成工具

发表于 : 2012-06-22 7:17
06_taro
最後,對於鄰域範圍的定義,我們之前只使用了mt_square這個函數來建立一個描述鄰域內3x3的部分,還有一次用到了"-1 -1 -1 0 -1 1 0 -1 0 1 1 -1 1 0 1 1"來描述in/deflate的鄰域範圍。現在來介紹一下這個鄰域像素的定義方式,以及masktools提供的其他預設好的描述鄰域範圍的函數。

先來看上面這個"-1 -1 -1 0 -1 1 0 -1 0 1 1 -1 1 0 1 1",它實際上是鄰域點相對於中心像素的坐標位置。我們將數組裡的值按兩個一組來劃分:
(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)
中心像素的坐標為(0, 0),所以上面這八組描述的就是中心像素周圍3x3的方塊內不包含中心像素自身的8個像素:

代码: 全选

(-1, -1), (0, -1), (1, -1)
(-1,  0),          (1,  0)
(-1,  1), (0,  1), (1,  1)
這就是鄰域像素點的定義模式。在mt_luts(x)裡的pixels與mt_ex/inpand裡的mode參數都是用這樣的相對坐標數組字符串來定義鄰域像素範圍的。

masktools預設了9個用於生成相對坐標數組用的模板函數:
mt_square(int "radius", bool "zero")
生成描述以中心像素為中心,邊長為2*radius+1的正方形的字符串。
mt_rectangle(int "hor_radius", int "ver_radius", bool "zero")
生成描述以中心像素為中心,水平邊長為2*hor_radius+1,垂直邊長為2*ver_radius+1的矩形的字符串。
mt_freerectangle(int "top_x", int "top_y", int "bottom_x", int "bottom_y", bool "zero")
生成描述任意相對坐標的,位置為從(top_x, top_y)到(bottom_x, bottom_y)範圍的矩形的字符串。

mt_diamond(int "radius", bool "zero")
生成描述以中心像素為中心,對角線長為2*radius+1的正菱形的字符串。
mt_losange(int "hor_radius", int "ver_radius", bool "zero")
生成描述以中心像素為中心,水平對角線長為2*hor_radius+1,垂直對角線長為2*ver_radius+1的菱形的字符串。
mt_freelosange(int "top_x", int "top_y", int "bottom_x", int "bottom_y", bool "zero")
生成描述任意相對坐標的,位置在(top_x, top_y)到(bottom_x, bottom_y)範圍的菱形(四個頂點為這個矩形範圍的各邊中點)的字符串。

mt_circle(int "radius", bool "zero")
生成描述以中心像素為圓心,半徑為radius的圓形的字符串。
mt_ellipse(int "hor_radius", int "ver_radius", bool "zero")
生成描述以中心像素為圓心,水平軸半徑為hot_radius,垂直軸半徑為ver_radius的橢圓形的字符串。
mt_freeellipse(int "top_x", int "top_y", int "bottom_x", int "bottom_y", bool "zero")
生成描述任意相對坐標的,位置在(top_x, top_y)到(bottom_x, bottom_y)範圍內橢圓形(與這個矩形範圍的四條邊相切)的字符串。

每個函數最後的bool "zero"參數表示的是十分包含(0, 0)這個中心像素,zero=true時包含,否則不包含。例如,前面的
"-1 -1 -1 0 -1 1 0 -1 0 1 1 -1 1 0 1 1"
可以寫作mt_square(1, zero=false),也可以寫作mt_rectangle(1, 1, zero=false),還可以寫作mt_freerectangle(-1, -1, 1, 1, zero=false)

讓我們來試著用這些函數做一些圖形看看:[syntax lang="avisynth"]
BlankClip(width=200, height=200, pixel_type="YV12")

mt_lutspa(expr="x 0.5 == y 0.5 == 255 0 ? 0 ?")
# 設定整個畫面中心的像素值爲255,其餘像素值爲0

ellipse = mt_lutxy(mt_expand(mode=mt_circle(85)), /* 最外邊的正圓 */
\ mt_expand(mode=mt_ellipse(85, 60)), /* 內切的橢圓 */
\ "x y -")

rect_los = mt_lutxy(mt_expand(mode=mt_freerectangle(-60, -40, 60, 40)), /* 內接的矩形 */
\ mt_expand(mode=mt_losange(60, 40)), /* 最裏邊的菱形 */
\ "x y -")

mt_lutxy(ellipse, rect_los, "x y +", chroma="128")[/syntax]
結果如圖所示:
图片

到這裡我們已經把masktools裡所有的函數都介紹完畢了,相信如果掌握了這裡所有的用法,應該已經可以完全了解mask、mt_lut、make/adddiff等核心函數的用途,以及mask的創建、處理、使用的過程,masktools對於數字圖像處理中專業性的知識要求僅集中在mt_edge、mt_lut、mt_convolution等少數函數中,而即使不具備這些知識,一般也能進行很多有效的處理。在使用過程中多熟悉各種函數的用法,在看別人的腳本過程中多注意masktools的應用,都會對masktools的掌握有所幫助。

本教程只涵蓋了最基礎的masktools的用法,但是配合官方文檔的說明,相信通過這些知識已經可以進階地練習一些中高級的應用,例如通過recursive的crop+mt_lut實現一個即可以像lut一樣使用像素值,也可以靠recursion過程傳遞過來的width/height參數像mt_lutspa一樣使用像素坐標,從而更加完善的mt_lutxspa等。希望大家能夠更好地挖掘masktools的可能性,在avs的視頻處理過程中取得更好的效果。

Re: 暫勿回复

发表于 : 2012-06-22 7:18
06_taro
Changelog:

2012.08.02
三處腳本錯誤修正,感謝Holy指出。果然順手寫出來不經檢查的東西一堆typo…

2012.08.01
5. 基於mt_lut的強大工具——make/adddiff部分修復第一個腳本例子裡漏的一個問號,感謝james1201指出

2012.07.09
1. 了解Masktools部分修正,Overlay內部處理不是在RGB下,而是在YUV 4:4:4下。說它內部RGB處理是我自己之前理解錯誤,在此對之前受誤導的表示歉意。感謝SAPikachu巨巨指正。
12. 輔助函數——鄰域字符串生成工具部分的例子添加代碼註釋。之前寫這段的時候忘了加註釋了…

2012.06.23
4. 深入淺出mt_lut(xy(z))部分增加對OP/ED進行AA時,利用credit mask,只處理背景畫面,而不影響credit的例子。

Re: 暫勿回复

发表于 : 2012-06-22 7:18
06_taro
佔樓備用~

Re: 暫勿回复

发表于 : 2012-06-22 7:19
06_taro
其實我想把前兩頁都佔滿…

Re: 暫勿回复

发表于 : 2012-06-22 7:19
06_taro
不過估計用不到那麼多備用的…

Re: 暫勿回复

发表于 : 2012-06-22 7:20
06_taro
所以就到此為止~

Re: 【教程】 MaskTools入門教程

发表于 : 2012-06-22 15:45
yabbay
このポストは人類の知恵の結晶でありんす
経験値急上昇の予感でありんす
さっそくipadに導入してじっくりがっくりと拝読させていただくでありんす {:cat_16}

Re: 【教程】 MaskTools入門教程

发表于 : 2012-06-22 15:46
ikamusume
{:cat_11} 好帖留名 {:cat_17}