隨著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;
}
}
}