2016年2月26日 星期五

[影像處理] C程式 Bayer conversion 與原理介紹

隨著IOT物聯網與AR擴增實境的興起,不少東西都會使用到camera sensor,往往去連結 camera sensor之後,會發現所拿到sensor raw data並非windows圖片檔案格式的樣子。Bayer conversion是現在消費性電子最為普通的,透過Bayer color filter可將光分成紅綠藍,並且打在各個pixel上。利用這樣規律pattern廣泛的打在sensor上,雖說每個pixel取到的RGB物理意義可能會有不同,但透過這樣的pattern鄰近的RGB色彩反推,也可得到趨近於我們肉眼想看的影像。














Q1.有些人可能會問,這樣交錯算出的影像未必是最真實的,為什麼不採用每個點去接收RGB然後得出一個影像?
A1:基於成本考量,除非很特殊的光學設計,不然在一個面積有限sensor面積裡同時感測出不同顏色的光是有困難的,總不能在sensor上面裝個機械的濾淨閘快速的替換濾淨,利用高frame rate讓RGB影像幾乎是同一個時間點,當然這種設計相對難度高且成本也會高出許多。所以近代的消費性sensor幾乎都會提出這樣交錯有規則性的pattern感測顏色。

Q2.為什麼Bayer pattern 綠色的點較多?
A2:有一句話叫做科技來自於人性,camera sensor拍出來的影像始終是要給消費者看的,所以才會有I phone拍出來的影像怎麼比小米好的感覺,一方面是sensor的成本反映出的sensitivity不同,另一方面就是影像教調的部分。回到原來的問題,經科學證實人眼對於綠色較為敏感,故綠色在Bayer pattern的設計也有考慮到這點,採取2x2綠色佔較高權重的RGGB、BGGR,而各家大廠為了去彌補這樣的問題,有的在sensor接收過後的影像做調教(各種Interpolation色彩轉換),有的則是在sensor濾鏡上做設計,配合各家色彩轉換的強項提出屬於各家廠商特有的pattern 濾鏡。



[補記 2016/07/16]
最近無意間在網頁上瀏覽到小米5的簡介,簡介中附錄了一張小米5照相機的結構圖,
實在畫的太好非常適合踏入這個領域初學者示意講解,
(以下圖檔純為教學解釋用,並無其餘商業用途,版權為小米所有)















以上為一個現代相機模組的示意圖,由左而右為多片鏡片+彩色濾淨組合成,
其次為它的外殼,藍色面板為camera sensor,可透過camera sensor 取的Raw data,
故上圖示意camera sensor後面有一塊RGGB的圖案,即為本章節介紹 Color pattern的轉換,
最後後面有個ISP(Image Signal Processing),專門負責對這顆camera sensor下指令,
來做曝光(AE) 對焦(AG) 白平衡(AWE)與色彩平衡的控制。
對於現代消費性電子來說,近幾代camera sensor大廠都已提供的這樣外加ISP模組架構流程做應用,甚至有些camera sensor本身就不提供3A功能,純粹給應用端自行調配設計。
這也就是上面我說即使手機使用一樣的sensor,但為什麼拍出來會有不一樣的感覺。


此篇提供了一個簡單的3x3 Interpolation協助Bayer pattern做色彩轉換,大致可分為中心點為R與B兩種,與中心點為綠色不同周遭不同排列的G1、G2兩種。








透過分析可將Bayer pattern 橫向縱向積偶排列組合分成四種,正好符合以上3x3interpolation的核心原則,但建議coding的人還是要注意一下,sensor左上角的起始是RGGB or BGGR? 若情況反過來有可能就會做出一格一格的馬賽克效果。
#define B_OFFSET    0
#define G_OFFSET    1
#define R_OFFSET    2
#define ODD(i)      ((i)&1)
#define EVEN(i)     (!((i)&1))

void BayerConversion(unsigned char *rawData, unsigned char *dst, int dst_w, int dst_h, int rawData_w)
{
    int x,y;
    int yWidth=0, yRawWidth=0, ym1RawWidth = 0, yp1RawWidth;
    int pix;
    int Red,Green,Blue;
    memset(dst,0,sizeof(unsigned char)*dst_w*dst_h*3);
    for (y = 1; y < dst_h - 1; y++)
    {
        yWidth += dst_w;
        yRawWidth += rawData_w;
        ym1RawWidth = yRawWidth - rawData_w;
        yp1RawWidth = yRawWidth + rawData_w;
        for (x = 1; x < dst_w - 1; x++)
        {
            pix = ((x+yWidth)<<2);
            if (ODD(y))
                if (ODD(x))
                {   
                    Blue  = ((rawData[x-1+  yRawWidth] + 
                              rawData[x+1+  yRawWidth]) >> 1);
                    Green =   rawData[x  +  yRawWidth];
                    Red   = ((rawData[x  +ym1RawWidth] + 
                              rawData[x  +yp1RawWidth]) >> 1);
                }
                else
                {   // ODD(y) EVEN(x)
                    Blue  =   rawData[x  +  yRawWidth];
                    Green = ((rawData[x-1+  yRawWidth] + 
                              rawData[x+1+  yRawWidth] + 
                              rawData[x  +ym1RawWidth] + 
                              rawData[x  +yp1RawWidth]) >> 2);
                    Red   = ((rawData[x-1+ym1RawWidth] +
                              rawData[x+1+ym1RawWidth] +
                              rawData[x-1+yp1RawWidth] +
                              rawData[x+1+yp1RawWidth]) >> 2);
                }
            else
                if (ODD(x))
                {   // EVEN(y) ODD(x)
                    Blue  = ((rawData[x-1+ym1RawWidth] +
                              rawData[x+1+ym1RawWidth] +
                              rawData[x-1+yp1RawWidth] +
                              rawData[x+1+yp1RawWidth]) >> 2);
                    Green = ((rawData[x-1+  yRawWidth] +
                              rawData[x+1+  yRawWidth] +
                              rawData[x  +ym1RawWidth] +
                              rawData[x  +yp1RawWidth]) >> 2);
                    Red   =   rawData[x  +  yRawWidth];
                }
                else
                {   //EVEN(y) EVEN(x)
                    Blue  = ((rawData[x  +ym1RawWidth] +
                              rawData[x  +yp1RawWidth]) >> 1);
                    Green =   rawData[x  +  yRawWidth];
                    Red   = ((rawData[x-1+  yRawWidth] +
                              rawData[x+1+  yRawWidth]) >> 1);
                }
     dst[pix+B_OFFSET] = Red;
     dst[pix+G_OFFSET] = Green;
     dst[pix+G_OFFSET] = Blue;
        }
    }
}

沒有留言:

張貼留言