1. 程式人生 > 實用技巧 >【短道速滑六】古老的視訊去噪演算法(FLT_GradualNoise)解析並優化,可實現1920*1080 YUV資料400fps的處理能力。

【短道速滑六】古老的視訊去噪演算法(FLT_GradualNoise)解析並優化,可實現1920*1080 YUV資料400fps的處理能力。

  這個好像沒有啥對應的論文可以找到,在百度上搜索也能找到一些相關的資料,不過就直接是程式碼,可以看到其實來自於一個叫做DScaler的專案,在github上目前還能找到該專案的完整資料。

  詳見:https://github.com/JohnAdders/DScaler/tree/f7d92b76678e24422c48d4a956c0486ee042786d

  其中含有FLT_GradualNoise.c檔案,我們複製以下程式碼的註釋部分對演算法的解釋:

This algorithm is very similar to what Andrew Dowsey came up with in his "AdaptiveTemporal Averaging" for his DirectShow filter. The algorithms differ in 1) theirblock size, 2) their motion estimation(sum of absolute differences versus meansquared error), 3) The addition of a "high tail," in which areas which have changeda lot(but not too much) still cause a small amount of averaging with the previousrame, and 4) rounding.

The algorithm :

This filter gets the sum of absolute differences between a four pixelhorizontal block in the current image and the same block in the precedingframe.This isn't the best local motion measure, but it's very fast due tothe psadbw SSE instruction.

This difference measure is used to determine the kind of averaging which will beconducted.If it's more than the "noise reduction" parameter, motion isinferred.In that case, we just use the new pixel values.If it's less than thenoise reduction, we use the ratio of(difference / noise reduction) to determine theweighting of the old and new values.

Somewhat more formally :

N = Sum_block(| oldByte - newByte | )

R = Noise Reduction parameter

M = (motion evidence) = 1 if N / R >= 1.2

0.999 if 1.2 > N / R >= 1

N / R otherwise

Result pixel = (bytewise)oldPixel * (1 - M) + newPixel * M

Rounding has a very significant effect on the algorithm.In general, forcomputational reasons, values are rounded down.An important exceptionoccurs when M > 0 and oldPixel != newPixel

but

oldPixel * (1 - M) + newPixel * M

rounds to oldPixel.In that case, the Result pixel is rounded to one towardthe newPixel value.This makes sure that very gradual variation is maintained.

針對這個演算法,作者提供了相關的彙編程式碼,而且進行了非常詳細的註釋,但是這個彙編還不是普通的彙編,而是用的SIMD指令,因此,對於閱讀來說就非常的困難了,我大概花了10天左右,理解其思路,並用更加容易東的Intrinsic進行了重寫和優化。下面是一些編寫時的疑惑和解讀,共享下。

//    疑點1: 對於YUV資料,這個程式是如何處理的? 
//    答覆:    從原始的彙編程式碼看,他對YUV分量是同步處理的,並沒有做特別的區分,前面說的四個畫素,指的意思就是Y0 U0 Y1 V0 Y2 U1 Y3 V1這4個畫素,不管是MMX指令還是SSE指令
//            他們的psadbw指令都是一次性執行八個位元組資料的絕對值累加(SSE指令一次性執行2個8個位元組的累加而已)。如果把這個演算法換成RGB格式的資料,那範圍要麻煩了,要拆分RGB到各個獨立的分量了。
//    疑點2: 上面提及預設的Rounding是向下的,但是一般要求只要Src和Prev有差異,就至少要向新畫素有1個畫素的偏移,以保證視訊的連續性,如何實現的。
//    答覆:    程式裡對資料進行了判斷,如果Src和Prev不同,則設定偏移量至少是1(正1和負1都可以),相同的話偏移量當然為0了。
//            另外,如果定點化後的偏移量大於65535,則設定偏移量為AbsDiff值,因為這個時候的由於程式移位計算的原因,直接算的值還會少1的。 (X * 65535) >> 16結果會為X - 1
//    疑點3: 程式是如何進行優化的?
//    答覆:  (1) 在原始的程式碼中,有這個0.999 if 1.2 > N / R >= 1,在作者提供的彙編程式碼中,對這部分做了處理,他是通過一些比較和移位來實現的,把NoiseMultiplier更改為65534了(N/R>=1,就已經設為65535了)
//            在本程式碼中,個人覺得這個判斷毫無必要,0.999對結果的影響太小了,因此捨棄了,在作者提供的SSE和MMX程式碼中,這個也捨棄了。
//            (2) 定點化,程式中N/R涉及到除法運算,為了減少這個,我們將整體擴大65536倍,然後再乘以AbsDiff,這個時候需要除以65536,這樣可以利用_mm_mulhi_epu16來快速實現(不需要特別的移位指令了,也不需要轉換到32位)
//            但是實際上,這裡是有誤差的,因為這個函式不能做到四捨五入,建議使用_mm_mulhrs_epi16代替。同時注意如果N/R * 65536如果大於65535了,就對於了原始算式中的M=1了 ,這個時候就把他直接限定為65535了(不需要轉換到32位了)
//            舉個例子,如果AbsDiff_Sum = 24,NoiseValue取值64,此時Multiplier的值為1024,則如果某個畫素的newPixel - oldPixel = 10,則結果為 (24 * 1024 * 10) >> 16 = 3,但是實際的浮點為3.75,理論上應該取4更為合適。
//            (3) oldPixel * (1 - M) + newPixel * M經過整理可以變為  oldPixel + (newPixel - oldPixel) * M, 此時配合newPixel - oldPixel的符號特性,可以使用_mm_adds_epu8和_mm_subs_epu8來實現最後的結果計算

  總的來說這個演算法,還是利用歷史幀的資料不斷的來平均誤差,減少視訊的噪音的,但是其可以充分利用快速計算8個位元組資料的累加值的指令_mm_sad_epu8,可以達到非常恐怖的計算效率和速度。

  測試1280*720大小視訊,去噪平均一幀約0.8ms,1920*1080視訊一幀需要約1.8ms(均位YUV422格式視訊)。

由於這裡上傳不了視訊,有需要了解該演算法效果的,可以單獨聯絡我,我可以提供個測試DEMO(DEMO太大,無法上傳),下面截兩張圖可以稍微看到區別。