)
1. 從Bayer陣列到全彩圖像Demosaic到底在做什么如果你拆開過任何一臺數(shù)碼相機或者手機攝像頭模組會看到CMOS/CCD傳感器表面覆蓋著一層彩色濾光片陣列最常見的就是Bayer模式——每個像素上方只允許紅、綠、藍其中一種顏色通過。以RGGB排列為例每4個像素里有1個紅、2個綠、1個藍。傳感器本身只輸出每個像素的單一顏色強度值也就是一張“半成品”的灰度圖。Demosaic去馬賽克要做的就是根據(jù)周圍像素的顏色信息推算出每個像素缺失的另外兩個通道值最終還原出每個像素都有完整RGB三通道的全彩圖像。這件事聽起來簡單做起來極其講究。因為缺失的兩個通道值并不存在只能“猜”——而怎么猜、猜得多準直接決定了最終圖像的清晰度、色彩還原度和邊緣質(zhì)量。Demosaic是ISP Pipeline中計算量最大、對畫質(zhì)影響最深的模塊之一也是區(qū)分一家ISP調(diào)試水平高低的關(guān)鍵環(huán)節(jié)。這篇文章適合幾類人看做ISP算法開發(fā)的工程師、FPGA圖像處理方向的從業(yè)者、用OpenCV做圖像處理項目的同學、以及剛接觸ISP調(diào)試需要理解Demosaic原理的入門者。我會從最基礎的雙線性插值一路講到邊緣自適應算法把每個階段的思路、參數(shù)、坑點都拆開講清楚。2. 為什么Demosaic不能隨便插值核心難點拆解2.1 顏色通道的空間采樣率不對等Bayer陣列的本質(zhì)問題是每個顏色通道的采樣密度不同。綠色通道有50%的采樣率RGGB中兩個G紅藍各只有25%。這意味著紅藍通道的空間分辨率天然比綠色低一半。如果直接對紅藍通道做簡單插值高頻細節(jié)會嚴重丟失表現(xiàn)為圖像邊緣出現(xiàn)彩色偽影。更麻煩的是人眼對綠色最敏感對紅藍的敏感度低一些但恰恰是紅藍通道的欠采樣最嚴重。所以Demosaic算法的核心挑戰(zhàn)就是如何在紅藍通道采樣率不足的情況下盡可能恢復出正確的高頻信息。2.2 邊緣區(qū)域的偽彩與拉鏈效應這是Demosaic最經(jīng)典的兩個問題。偽彩false color出現(xiàn)在高頻紋理區(qū)域比如細密的黑白條紋插值算法會“誤判”出紅藍交替的顏色。拉鏈效應zipper effect出現(xiàn)在水平或垂直邊緣處插值方向選擇錯誤會導致邊緣像拉鏈一樣鋸齒狀。這兩個問題的根源是一樣的算法在邊緣處無法正確判斷插值方向。如果水平邊緣用了垂直方向的像素來插值結(jié)果必然出錯。所以所有優(yōu)秀的Demosaic算法核心都在做一件事——準確判斷邊緣方向然后沿邊緣方向插值。2.3 計算復雜度與實時性的矛盾高端Demosaic算法比如基于方向濾波、自適應權(quán)重、甚至機器學習的方法效果很好但計算量巨大。在手機ISP里Demosaic必須在每幀幾毫秒內(nèi)完成功耗還要控制在極低水平。FPGA實現(xiàn)時行緩存line buffer的數(shù)量、乘法器的用量、流水線的設計都直接決定了能不能跑到目標幀率。這就是為什么實際產(chǎn)品中用的Demosaic算法往往是效果和復雜度的折中方案。3. Demosaic技術(shù)演進從雙線性到邊緣自適應3.1 第一代雙線性插值——簡單但問題多雙線性插值是最直觀的方法。以RGGB陣列為例對于每個像素用周圍同色像素的加權(quán)平均來估算缺失通道。比如一個R像素位置G值用上下左右四個G像素的平均B值用對角線四個B像素的平均。這個方法計算量極小硬件實現(xiàn)只需要加法器和移位器。但問題也很明顯邊緣處模糊嚴重偽彩和拉鏈效應都很明顯。實測下來雙線性插值在ISO 100的平滑場景還能看一旦到了高ISO或者有細密紋理的場景畫質(zhì)直接崩。我早期做FPGA ISP驗證時第一版就是用雙線性插值跑通的。當時用了一張分辨率測試卡雙線性插值出來的圖像在1000線對以上的區(qū)域已經(jīng)完全糊成一片彩色摩爾紋滿屏都是。這個方案只適合用來驗證數(shù)據(jù)通路是否正確絕對不能作為最終方案。3.2 第二代色差恒定假設與梯度修正后來大家發(fā)現(xiàn)一個規(guī)律在局部小區(qū)域內(nèi)R通道和G通道的差值R-G變化比較平緩B-G也是。這就是色差恒定假設。基于這個假設可以先插值G通道因為G采樣率最高插值最準然后用G通道加上色差來估算R和B。具體做法是先對G通道做方向自適應插值得到全分辨率的G。然后計算已知R位置上的R-G差值對這個差值圖做插值最后用插值后的色差加上G得到全分辨率的R。B通道同理。這個方法比雙線性好很多因為色差圖比原始通道平滑得多插值引入的誤差更小。但它在強邊緣處仍然會出問題因為色差恒定假設在邊緣處不成立。3.3 第三代方向自適應與邊緣感知這是目前主流ISP中廣泛使用的方法。核心思路是在插值之前先判斷當前像素處是否存在邊緣以及邊緣的方向。如果水平梯度小于垂直梯度說明邊緣更可能是水平方向那就沿水平方向插值反之沿垂直方向。梯度計算通常用Sobel算子或者簡單的差分。以G通道插值為例在R像素位置計算水平方向梯度dh和垂直方向梯度dvdh |G_left - G_right| |2R_center - R_left2 - R_right2|dv |G_up - G_down| |2R_center - R_up2 - R_down2|然后根據(jù)dh和dv的大小關(guān)系選擇水平或垂直方向的G像素做插值或者按權(quán)重加權(quán)。這個方法的偽彩和拉鏈效應比前兩代好很多但梯度計算本身對噪聲敏感。在高ISO場景下噪聲會導致梯度判斷錯誤反而引入新的偽彩。所以實際實現(xiàn)中通常要先做降噪或者對梯度做閾值限制。3.4 第四代頻域分離與殘差插值近幾年比較前沿的思路是把圖像分解成低頻和高頻兩部分。低頻部分用簡單的雙線性插值就夠了因為低頻信號空間變化緩慢。高頻部分則用更精細的方法處理比如基于稀疏表示或者導向濾波。具體流程是先對Bayer圖像做低通濾波得到低頻分量插值后得到低頻全彩圖。然后用原始Bayer減去低頻得到高頻殘差。對高頻殘差做方向自適應插值最后把低頻和高頻合并。這個思路的好處是把插值的難度降低了——低頻插值幾乎不會出錯高頻插值雖然難但殘差信號幅度小即使出錯也不明顯。實測在紋理豐富的場景下這個方法比純方向自適應能提升1-2dB的PSNR。3.5 第五代基于學習的Demosaic用CNN做Demosaic是近幾年的熱點?;舅悸肥悄么罅緽ayer-全彩圖像對訓練網(wǎng)絡讓網(wǎng)絡學會從Bayer模式恢復全彩圖像。網(wǎng)絡結(jié)構(gòu)通常比較輕量因為Demosaic是一個局部操作不需要很大的感受野。但這里有個實際問題訓練數(shù)據(jù)通常是合成的用全彩圖下采樣得到Bayer而真實傳感器的噪聲特性、壞點、顏色串擾都和合成數(shù)據(jù)不一樣。所以純學習方法在實驗室指標很好看實際ISP中往往需要和傳統(tǒng)方法結(jié)合使用。目前產(chǎn)品中見到的方案大多是“傳統(tǒng)算法為主 學習做后處理修正”的混合架構(gòu)。4. 邊緣優(yōu)化策略把偽彩和拉鏈壓到最低4.1 梯度計算的噪聲抑制技巧梯度計算是邊緣自適應Demosaic的第一步也是最容易出問題的一步。原始梯度對噪聲極其敏感一個孤立的噪聲點就能讓梯度判斷完全反轉(zhuǎn)。實際實現(xiàn)中必須做處理。我常用的做法是梯度計算前先對G通道做一次3x3的中值濾波或者用可分離的均值濾波。中值濾波對椒鹽噪聲效果好但硬件實現(xiàn)需要排序網(wǎng)絡資源消耗大。均值濾波簡單但會模糊邊緣。折中方案是用加權(quán)均值中心像素權(quán)重高周圍權(quán)重低。另一個技巧是給梯度加一個偏置項。比如dh dh_raw offsetdv dv_raw offset。這個offset的作用是當兩個方向梯度都很小平坦區(qū)域時讓算法傾向于某個默認方向避免在平坦區(qū)域頻繁切換方向?qū)е聜尾省ffset的值需要根據(jù)噪聲水平調(diào)整噪聲大時offset也大。4.2 插值方向的軟判決與加權(quán)融合硬判決直接選水平或垂直在邊緣方向不明確時容易出錯。更好的做法是軟判決根據(jù)梯度比值計算兩個方向的權(quán)重然后加權(quán)融合。權(quán)重計算公式通常長這樣w_h dv / (dh dv)w_v dh / (dh dv)這樣當dh遠小于dv時w_h接近1主要用水平插值反之亦然。當dh和dv接近時兩個方向各占一半。這個方法比硬判決平滑得多過渡區(qū)域不會出現(xiàn)突變。但要注意權(quán)重計算涉及除法硬件實現(xiàn)時通常用查找表或者近似計算。我一般會把dh和dv量化到5-6bit然后查表得到權(quán)重這樣資源消耗可控。4.3 偽彩抑制的后處理即使Demosaic算法本身做得很好高頻區(qū)域仍然可能殘留偽彩。這時候需要后處理來壓制。常用的方法是把圖像轉(zhuǎn)換到Y(jié)UV或者Lab空間對色度通道做低通濾波。因為偽彩主要表現(xiàn)為色度的劇烈變化而真實邊緣的色度變化通常比較平緩。對色度通道做濾波可以在不影響亮度清晰度的前提下壓制偽彩。但這個方法有個副作用真實的高飽和度邊緣比如紅色物體和藍色背景的交界色度也會被模糊。所以濾波強度需要根據(jù)局部梯度自適應調(diào)整——梯度大的地方少濾梯度小的地方多濾。4.4 拉鏈效應的定向修復拉鏈效應表現(xiàn)為邊緣處的鋸齒。修復思路是檢測出拉鏈區(qū)域然后沿邊緣方向做中值濾波。檢測方法計算當前像素與水平/垂直鄰居的差值如果某個方向的差值顯著大于另一個方向說明該方向可能存在拉鏈。然后沿邊緣方向取3個或5個像素做中值替換當前像素。這個方法對已經(jīng)產(chǎn)生的拉鏈有效但更好的策略是在Demosaic階段就避免拉鏈產(chǎn)生。關(guān)鍵還是梯度判斷要準插值方向要穩(wěn)。5. FPGA實現(xiàn)Demosaic的工程要點5.1 行緩存與流水線設計FPGA做Demosaic第一個要解決的問題是行緩存。以5x5窗口為例至少需要緩存4行圖像數(shù)據(jù)。每行緩存的深度等于圖像寬度。對于4K圖像3840像素寬每行緩存需要3840 x 像素位寬。如果像素是12bit一行就是46Kbit4行就是184Kbit。這還只是數(shù)據(jù)緩存還沒算梯度計算、權(quán)重查找表等中間結(jié)果。實際設計中我通常會用雙端口BRAM來做行緩存寫端口接傳感器數(shù)據(jù)流讀端口給Demosaic計算單元。讀寫地址的生成邏輯要仔細設計確保不會出現(xiàn)讀寫沖突。流水線方面Demosaic的計算可以分成幾級第一級計算梯度第二級計算權(quán)重第三級做插值第四級做后處理。每級之間用寄存器打拍這樣可以跑到比較高的時鐘頻率。但要注意流水線延遲對后續(xù)模塊的影響尤其是需要和原始Bayer對齊的模塊。5.2 定點化與精度權(quán)衡FPGA里做浮點運算代價太高必須定點化。梯度計算、權(quán)重計算、插值系數(shù)都需要確定位寬。我的經(jīng)驗是梯度用12-14bit權(quán)重用8-10bit插值系數(shù)用8bit。插值結(jié)果的精度損失控制在0.5LSB以內(nèi)。具體位寬需要根據(jù)傳感器位深和畫質(zhì)要求來定。12bit傳感器的話中間結(jié)果至少保留14bit最終輸出截到12bit。定點化最容易出問題的地方是除法。權(quán)重計算里的除法如果直接用整數(shù)除法精度損失很大。我一般會用倒數(shù)查找表把分母量化到256個檔位預計算倒數(shù)表然后分子乘以倒數(shù)再移位。這樣精度和資源都能接受。5.3 與ISP Pipeline其他模塊的配合Demosaic不是孤立的模塊。它前面通常有黑電平校正、鏡頭陰影校正、壞點矯正后面有顏色校正、Gamma、銳化。這些模塊都會影響Demosaic的效果。比如壞點矯正沒做好壞點會導致梯度計算錯誤進而產(chǎn)生偽彩。所以壞點矯正必須在Demosaic之前而且要做干凈。鏡頭陰影校正會改變圖像的空間亮度分布如果校正過度邊緣區(qū)域的梯度會被放大Demosaic的方向判斷也會受影響。我調(diào)試時習慣把Demosaic單獨拿出來看效果用標準測試圖比如ISO 12233分辨率卡、ColorChecker評估。確認Demosaic本身沒問題后再放回Pipeline里看整體效果。6. 常見問題與排查技巧實錄6.1 偽彩問題排查速查表現(xiàn)象可能原因排查方法解決思路高頻區(qū)域彩色摩爾紋梯度判斷錯誤關(guān)閉方向自適應改用雙線性對比增大梯度偏置加強降噪邊緣處紅藍交替色差假設失效檢查邊緣處R-G和B-G的變化引入邊緣感知的色差修正整體偏色通道增益不匹配拍灰卡檢查白平衡在Demosaic前做白平衡暗部彩色噪點噪聲導致梯度翻轉(zhuǎn)提高ISO拍暗場梯度計算前做降噪6.2 拉鏈效應排查拉鏈效應通常出現(xiàn)在水平或垂直邊緣處。排查時先確認邊緣方向然后檢查該方向的梯度計算是否正確。我遇到過一次案例FPGA實現(xiàn)時行緩存的讀寫地址差了一位導致垂直方向的梯度計算用錯了像素拉鏈效應嚴重。這種問題只能靠仿真波形逐拍排查。另一個常見原因是插值方向硬判決的閾值設置不合理。閾值太小平坦區(qū)域頻繁切換方向閾值太大邊緣處判斷遲鈍。我一般會先用MATLAB仿真掃一遍閾值找到偽彩和拉鏈的平衡點再移植到FPGA。6.3 高ISO下的Demosaic退化高ISO時噪聲大Demosaic效果會明顯下降。這時候不能只調(diào)Demosaic參數(shù)要從整個Pipeline考慮。通常的做法是高ISO時降低Demosaic的方向自適應強度更多依賴色差插值同時加強后續(xù)的色度降噪。我實測過在ISO 3200下如果把Demosaic的方向判斷閾值調(diào)高更傾向于雙線性偽彩會少很多但清晰度會下降。這時候用后續(xù)的銳化模塊補一點清晰度整體效果比強行用方向自適應要好。6.4 與壞點矯正的相互影響壞點矯正不干凈是Demosaic偽彩的常見原因。一個壞點會導致周圍4個像素的梯度計算全部出錯。排查方法是關(guān)閉壞點矯正看偽彩是否加重如果加重說明壞點矯正在起作用但不夠干凈如果不變說明偽彩不是壞點引起的。壞點矯正本身也有坑。有些壞點矯正算法會誤判高頻細節(jié)為壞點把正常的紋理抹掉。這時候Demosaic拿到的就是被破壞的Bayer數(shù)據(jù)怎么插值都救不回來。所以壞點矯正的閾值要調(diào)得保守一些寧可漏掉幾個壞點不要誤殺細節(jié)。7. 用OpenCV和MATLAB快速驗證Demosaic算法7.1 OpenCV中的Demosaic函數(shù)OpenCV提供了cvtColor函數(shù)可以把Bayer圖像轉(zhuǎn)換成RGB。支持的Bayer模式包括BG、GB、RG、GR四種排列。用法很簡單import cv2 import numpy as np # 讀取Bayer圖像假設是16bit RAW bayer cv2.imread(raw.tiff, cv2.IMREAD_UNCHANGED) # 轉(zhuǎn)換為RGB使用雙線性插值 rgb_bilinear cv2.cvtColor(bayer, cv2.COLOR_BayerBG2RGB) # 使用邊緣自適應算法OpenCV 4.x支持 rgb_ea cv2.cvtColor(bayer, cv2.COLOR_BayerBG2RGB_EA)COLOR_BayerBG2RGB_EA用的是邊緣自適應算法效果比雙線性好不少。但OpenCV的實現(xiàn)在高噪聲下也會出偽彩適合做快速驗證不適合直接用于產(chǎn)品。7.2 MATLAB仿真框架MATLAB做Demosaic仿真更靈活可以自己寫算法逐行調(diào)試。我通常的框架是讀入全彩圖像下采樣成Bayer實現(xiàn)自己的Demosaic算法和原始全彩圖對比計算PSNR和SSIM可視化偽彩和拉鏈區(qū)域關(guān)鍵函數(shù)是demosaicMATLAB自帶的版本支持幾種插值方法。但做算法研究時我一般自己寫方便改參數(shù)。% 讀取全彩圖并生成Bayer rgb imread(test.png); bayer raw2bayer(rgb, rggb); % 自定義Demosaic rgb_out my_demosaic(bayer, rggb); % 計算指標 psnr_val psnr(rgb_out, rgb); ssim_val ssim(rgb_out, rgb);7.3 評估指標的選擇PSNR和SSIM是常用指標但它們對偽彩不敏感。評估Demosaic效果時我還會看色差圖的統(tǒng)計特性。具體做法是把輸出圖像和參考圖像都轉(zhuǎn)換到Lab空間計算ab通道的差值圖看差值圖的均值和方差。偽彩嚴重時ab差值的方差會明顯增大。另一個實用指標是邊緣處的彩色誤差。用Canny檢測參考圖的邊緣然后在邊緣區(qū)域計算色差。這個指標比全局PSNR更能反映Demosaic的實際觀感。8. 調(diào)試ISP Demosaic的實戰(zhàn)心得Demosaic調(diào)試最忌諱只看實驗室指標。我踩過最大的坑是在MATLAB里PSNR刷到42dB移植到FPGA后實際拍圖一看暗部全是彩色噪點。原因是MATLAB仿真用的Bayer是理想下采樣得到的沒有噪聲、沒有壞點、沒有顏色串擾。真實傳感器出來的數(shù)據(jù)完全不是那么回事。后來我養(yǎng)成了一個習慣所有Demosaic算法必須用真實RAW數(shù)據(jù)驗證。哪怕只有幾張不同場景的RAW白天、夜晚、室內(nèi)、室外也比合成數(shù)據(jù)有參考價值。真實RAW里能看到噪聲、壞點、鏡頭陰影對Demosaic的實際影響這些是仿真里模擬不出來的。另一個心得是Demosaic的參數(shù)不要一次調(diào)太多。梯度偏置、權(quán)重曲線、后處理強度這些參數(shù)相互耦合。我一般固定其他參數(shù)只調(diào)一個看效果變化找到拐點后再調(diào)下一個。全部調(diào)完后再聯(lián)合微調(diào)一遍。還有一點不同傳感器的Bayer排列可能不同。RGGB、BGGR、GRBG、GBRG四種排列如果搞錯了出來的圖像顏色完全不對。調(diào)試前先用一張純紅或純藍的圖確認Bayer排列這個步驟不能省。最后分享一個快速判斷Demosaic好壞的技巧拍一張包含細密黑白條紋的場景比如分辨率測試卡放大到200%看條紋邊緣。好的Demosaic條紋邊緣干凈沒有彩色鑲邊差的Demosaic條紋上會爬滿紅藍交替的偽彩。這個方法比看PSNR直觀得多也快得多。