表+動態(tài)檢測的互補策略——從單像素壞點到cluster壞點的檢測算法設(shè)計與極限case處理)
044、壞點校正的漏檢與誤殺——靜態(tài)表動態(tài)檢測的互補策略——從單像素壞點到cluster壞點的檢測算法設(shè)計與極限case處理昨晚在實驗室盯著一塊Sensor的RAW圖越看越覺得不對勁。暗場下明明做了壞點校正畫面里卻還是星星點點地閃著白點不是那種均勻的噪點是那種固定位置、固定亮度的“釘子戶”。更頭疼的是同一顆Sensor在另一臺樣機上表現(xiàn)正常換到這臺就冒出來十幾個新壞點??蛻裟沁叺姆答伕苯幽銈冞@個壞點校正是不是擺設(shè)這種問題我遇到過太多次了。壞點校正DPCDefect Pixel Correction在ISP pipeline里是個不起眼的模塊但恰恰是它最容易在量產(chǎn)階段翻車。翻車的模式就兩種漏檢——該修的沒修畫面里殘留固定亮點誤殺——不該修的修了把正常的像素點給抹掉了畫面出現(xiàn)“死灰”或者細(xì)節(jié)丟失。而這兩者往往是矛盾的你為了減少漏檢把檢測閾值調(diào)低誤殺率立刻飆升反過來閾值調(diào)高漏檢又壓不住。先說漏檢。靜態(tài)壞點表Static Defect Table是出廠前燒錄的基于Sensor廠商提供的測試數(shù)據(jù)。但問題在于Sensor的壞點不是一成不變的。溫度漂移、電壓波動、長時間使用后的老化都會產(chǎn)生新的壞點。尤其是暗電流隨溫度升高而增大某些像素在常溫下正常到了高溫環(huán)境就變成“熱像素”亮度值明顯高于周圍。這種動態(tài)壞點靜態(tài)表根本管不到。再說誤殺。動態(tài)檢測算法通?;卩徲虮容^比如當(dāng)前像素與周圍8鄰域的差值超過閾值就判為壞點。但遇到真實圖像中的高對比度邊緣、細(xì)紋理、或者高光區(qū)域這種檢測很容易誤判。比如一根白色細(xì)線穿過畫面線上的像素點與兩側(cè)暗區(qū)的差值極大算法一看哎喲全是壞點全給抹了。結(jié)果就是畫面里的細(xì)線變成了虛線細(xì)節(jié)全丟。我見過最極端的case是某款車載攝像頭在夜間行車時路燈的高光區(qū)域被動態(tài)檢測誤殺導(dǎo)致畫面出現(xiàn)一圈一圈的“黑暈”客戶直接投訴說你們這個攝像頭是不是壞了。后來排查發(fā)現(xiàn)問題出在檢測算法只用了單幀的鄰域差值沒有考慮時間域的一致性。真正的壞點是固定的每幀都在同一位置異常而高光邊緣是隨場景移動的不會固定在某個像素上。所以正確的思路是靜態(tài)表動態(tài)檢測互補。靜態(tài)表負(fù)責(zé)處理出廠已知的壞點動態(tài)檢測負(fù)責(zé)捕捉運行過程中新出現(xiàn)的壞點。但動態(tài)檢測不能只看單幀必須結(jié)合多幀時間域信息。這里有個關(guān)鍵設(shè)計動態(tài)壞點的判定需要連續(xù)N幀在同一位置都出現(xiàn)異常才確認(rèn)為壞點。N的取值很講究太小了誤殺率高太大了漏檢率高。我一般建議N取3到5幀具體要看幀率和場景運動速度。車載場景幀率30fpsN取3比較合適安防場景幀率可能只有15fpsN取2到3。但多幀檢測也有坑。如果場景本身是靜止的比如安防監(jiān)控對著一個固定區(qū)域那么畫面中的噪點也會在固定位置出現(xiàn)多幀容易被誤判為壞點。這時候需要引入一個“噪聲容忍度”的概念——不是看絕對差值而是看相對信噪比。具體做法是先計算當(dāng)前像素與鄰域均值的差值再除以鄰域標(biāo)準(zhǔn)差得到一個歸一化的偏離度。只有偏離度超過某個閾值并且連續(xù)多幀都超過才判定為壞點。這個閾值我通常設(shè)在5到6個標(biāo)準(zhǔn)差太低會誤殺太高會漏檢。再說cluster壞點。單像素壞點好處理但Sensor制造過程中可能出現(xiàn)一片區(qū)域的多個像素同時損壞形成2x2、3x3甚至更大的壞點簇。這種cluster壞點如果按單像素處理會出現(xiàn)一個嚴(yán)重問題壞點簇內(nèi)部的像素互相作為鄰域參考導(dǎo)致檢測不到。比如一個3x3的壞點簇中心像素的8鄰域全是壞點鄰域均值本身就被污染了差值計算出來反而不大漏檢了。處理cluster壞點我常用的方法是分級檢測。第一級用大窗口比如5x5或7x7計算鄰域統(tǒng)計值找出疑似壞點區(qū)域第二級在疑似區(qū)域內(nèi)做細(xì)粒度檢測區(qū)分單點壞和cluster壞。大窗口的好處是即使壞點簇內(nèi)部有污染窗口邊緣的正常像素仍然能提供參考。但大窗口也有問題——如果壞點簇太大比如超過5x5大窗口也被污染了。這時候需要引入“方向性檢測”分別計算水平、垂直、兩個對角線方向的梯度取最小值作為參考。因為壞點簇通常是各向同性的而真實圖像中的邊緣是各向異性的方向性檢測可以區(qū)分這兩者。這里踩過一個大坑。某次在工業(yè)視覺項目里客戶的產(chǎn)品表面有規(guī)則的網(wǎng)格紋理網(wǎng)格交叉點的像素亮度極高。我們的動態(tài)檢測算法把這些交叉點全判成了壞點導(dǎo)致校正后網(wǎng)格紋理變得模糊。后來分析發(fā)現(xiàn)網(wǎng)格交叉點的亮度雖然高但在時間域上是穩(wěn)定的而且方向性梯度在水平和垂直方向都很大但在對角線方向很小。真正的壞點應(yīng)該是所有方向梯度都異常大。于是我們加了一個條件只有四個方向梯度都超過閾值才判定為壞點。這樣網(wǎng)格交叉點就被排除了。極限case處理我總結(jié)了幾類。第一類是Sensor輸出飽和——當(dāng)場景過曝時所有像素都達(dá)到最大值這時候任何檢測算法都會失效。處理辦法是檢測到飽和區(qū)域后跳過該區(qū)域的壞點檢測只做靜態(tài)表校正。第二類是Sensor輸出全黑——暗場下所有像素值都很低動態(tài)檢測的閾值需要動態(tài)調(diào)整不能用一個固定值。我通常的做法是根據(jù)當(dāng)前幀的全局統(tǒng)計值均值、方差自適應(yīng)調(diào)整檢測閾值。第三類是壞點恰好位于圖像邊緣——鄰域像素不足無法計算統(tǒng)計值。處理辦法是邊緣像素只做靜態(tài)表校正不做動態(tài)檢測或者用鏡像填充的方式擴(kuò)展鄰域。還有一個容易被忽略的點壞點校正的順序。在ISP pipeline里壞點校正通常放在黑電平校正之后、去噪之前。如果放在去噪之后去噪算法會把壞點的能量擴(kuò)散到周圍像素導(dǎo)致壞點特征被掩蓋檢測難度大增。如果放在黑電平之前暗電流的影響還沒被去除檢測閾值不好設(shè)。所以順序很重要別亂調(diào)。代碼實現(xiàn)上動態(tài)檢測的核心邏輯大概是這樣的// 這里踩過坑別用int存像素差值會溢出// 用int32_t特別是處理12bit RAW時int32_tdiff[4];// 四個方向的梯度int32_tmin_gradINT32_MAX;// 計算四個方向的梯度取最小值// 方向1水平diff[0]abs(pixel-left)abs(pixel-right);// 方向2垂直diff[1]abs(pixel-up)abs(pixel-down);// 方向3主對角線diff[2]abs(pixel-up_left)abs(pixel-down_right);// 方向4副對角線diff[3]abs(pixel-up_right)abs(pixel-down_left);for(inti0;i4;i){if(diff[i]min_grad){min_graddiff[i];}}// 歸一化除以鄰域標(biāo)準(zhǔn)差// 別用全局標(biāo)準(zhǔn)差要用局部窗口的floatnorm_grad(float)min_grad/local_std;// 多幀一致性判斷// 這里用環(huán)形緩沖區(qū)存歷史結(jié)果別用數(shù)組拷貝浪費帶寬if(norm_gradthresholdhistory[frame_idx%N]1){// 連續(xù)N幀都異常確認(rèn)為壞點pixelmedian_filter(pixel,window);}最后說點個人經(jīng)驗。壞點校正這個模塊看起來簡單但真正做好很難。我見過太多團(tuán)隊在這個模塊上栽跟頭原因都是只盯著單幀的檢測算法忽略了時間域和空間域的聯(lián)合分析。我的建議是第一靜態(tài)表一定要做而且要定期更新——量產(chǎn)后的老化測試數(shù)據(jù)要回流到靜態(tài)表里第二動態(tài)檢測一定要結(jié)合多幀單幀檢測就是耍流氓第三閾值參數(shù)不要拍腦袋定要基于大量實拍數(shù)據(jù)做統(tǒng)計分析找到合理的分布區(qū)間第四一定要留調(diào)試接口把檢測到的壞點位置和置信度輸出到日志里方便現(xiàn)場排查。壞點校正不是一錘子買賣它是Sensor生命周期里持續(xù)演進(jìn)的過程。你今天調(diào)好的參數(shù)明天換個溫度環(huán)境可能就失效了。所以設(shè)計算法的時候就要想著怎么自適應(yīng)、怎么自學(xué)習(xí)而不是靠一堆硬編碼的閾值撐場面。