Bayer图像根据实际探测器不同颜色分量的排列方式可以分为4种格式:RGGB、GRBG、BGGR、GBRG,分别对应OpenCV里的BayerBG、BayerGB、BayerRG、BayerGR格式。如下图所示:
常用的Bayer转换算法是对图像的每个像素位置取其3×3邻域内的相同分量对当前位置缺少的分量进行估计,从而得到3通道的彩色图像。以BayerBG格式的图像为例,在Bayer图像中,所有的像素位置其插值方式可分为4种,分别为:偶数行偶数列、偶数行奇数列、奇数行偶数列、奇数行奇数列(按C++的风格,这里行列的起始均为0)。
例如,
1)对于偶数行偶数列(row=2,col=2),当前Bayer图像该位置为B分量,其R分量可以由Bayer图像3×3邻域的4个R分量取均值进行估计,G分量可以由Bayer图像3×3邻域的4个G分量取均值进行估计。
2)对于偶数行奇数列(row=2,col=3),当前Bayer图像该位置为G分量,其R分量可以由Bayer图像3×3邻域的2个R分量取均值进行估计,B分量可以由Bayer图像3×3邻域的2个B分量取均值进行估计。
3)对于奇数行偶数列(row=3,col=2),当前Bayer图像该位置为G分量,其R分量可以由Bayer图像3×3邻域的2个R分量取均值进行估计,B分量可以由Bayer图像3×3邻域的2个B分量取均值进行估计。
4)对于奇数行奇数列(row=3,col=2),当前Bayer图像该位置为R分量,其G分量可以由Bayer图像3×3邻域的4个G分量取均值进行估计,B分量可以由Bayer图像3×3邻域的4个B分量取均值进行估计。
针对BayerBG格式转RGB图像的代码如下:
/**
* @brief BayerBG2RGB,BayerBG格式图像转为RGB图像
* @param src,输入图像,CV_8UC1格式Bayer图像
* @param dst,输出图像,CV_8UC3格式彩色图像
*/
void BayerBG2RGB(const cv::Mat& src,cv::Mat& dst)
{
//BG格式的Bayer图像排列如下
/*
* B G B G B G B
* G R G R G R G
* B G B G B G B
* G R G R G R G
* B G B G B G B
* G R G R G R G
* B G B G B G B
*/
//按3×3的邻域处理,边缘需填充至少1个像素的宽度
int nBorder = 1;
cv::Mat bayer;
cv::copyMakeBorder(src,bayer,nBorder,nBorder,nBorder,nBorder,cv::BORDER_REPLICATE);
cv::Mat rgb(bayer.size(),CV_8UC3);
rgb.setTo(cv::Scalar(0,0,0));
uchar* pBayer = (uchar*)bayer.ptr();
uchar* pRGB = (uchar*)rgb.ptr();
int nW = bayer.cols;
int nH = bayer.rows;
//注意:OpenCV中约定用Mat存储3通道彩色图像时,第0通道为B分量,第1通道表示G分量,第2通道表示R分量
//在从cv::cvtColor函数的注释中有如下解释:
/* Note that the default color format in OpenCV is often referred to as RGB but it is actually BGR (the
* bytes are reversed). So the first byte in a standard (24-bit) color image will be an 8-bit Blue
* component, the second byte will be Green, and the third byte will be Red.
*/
for(int i=nBorder; i<nH-nBorder; i++)
{
for(int j=nBorder; j<nW-nBorder; j++)
{
//3×3邻域像素定义
/*
* |M00 M01 M02|
* |M10 M11 M12|
* |M20 M21 M22|
*/
int nM00 = (i-1)*nW+(j-1); int nM01 = (i-1)*nW+(j+0); int nM02 = (i-1)*nW+(j+1);
int nM10 = (i-0)*nW+(j-1); int nM11 = (i-0)*nW+(j+0); int nM12 = (i-0)*nW+(j+1);
int nM20 = (i+1)*nW+(j-1); int nM21 = (i+1)*nW+(j+0); int nM22 = (i+1)*nW+(j+1);
if(i%2 == 0)
{
if(j%2 == 0) //偶数行偶数列
{
pRGB[i*nW*3+j*3+0]=pBayer[nM11];//b
pRGB[i*nW*3+j*3+2]=((pBayer[nM00]+ pBayer[nM02]+pBayer[nM20]+pBayer[nM22])>>2);//r
pRGB[i*nW*3+j*3+1]=((pBayer[nM01]+ pBayer[nM10]+pBayer[nM12]+pBayer[nM21])>>2);//g
}
else //偶数行奇数列
{
pRGB[i*nW*3+j*3+1]=pBayer[nM11];//g
pRGB[i*nW*3+j*3+2]=(pBayer[nM01]+pBayer[nM21])>>1;//r
pRGB[i*nW*3+j*3+0]=(pBayer[nM10]+pBayer[nM12])>>1;//b
}
}
else
{
if(j%2 == 0) //奇数行偶数列
{
pRGB[i*nW*3+j*3+1]=pBayer[nM11];//g
pRGB[i*nW*3+j*3+2]=(pBayer[nM10]+pBayer[nM12])>>1;//r
pRGB[i*nW*3+j*3+0]=(pBayer[nM01]+pBayer[nM21])>>1;//b
}
else //奇数行奇数列
{
pRGB[i*nW*3+j*3+2]=pBayer[nM11];//r
pRGB[i*nW*3+j*3+1]=(pBayer[nM01]+pBayer[nM21]+pBayer[nM10]+pBayer[nM12])>>2;//g
pRGB[i*nW*3+j*3+0]=(pBayer[nM00]+pBayer[nM02]+pBayer[nM20]+pBayer[nM22])>>2;//b
}
}
}
}
dst = rgb(cv::Rect(nBorder,nBorder,src.cols,src.rows)).clone();
}
作者将其结果与OpenCV的cvtColor函数的处理结果进行了比较,视觉上没有发现明显差异。但是,在边缘处理上,cvtColor函数的效果更好,暂未明确其内部处理机制。