xfopencv提供了基于xfmat进行操作的demosac函数。
但是这里,我们要基于该函数的设计思想,移植适合项目的bayer_to_rgb函数。
++++++++++++++++++++++++++++++++++++++++++++++++++++++
首先我们需要几个核算子函数。
1)kernel_RB_G
short kernel_RB_G(u8 block[5][5])
{
/*
0 1 2 3 4
R G R G R 0
G B G B G 1
R G R G R 2
G B G B G 3
R G R G R 4
*/
//y在前,x在后
//hd = |G21 - G23| + |R22 * 2 - R20 -R24|
//vd = |G12 - G32| + |R22 * 2 - R02 -R42|
//gh = (G21 + G23) / 2 + (R22 * 2 - R20 -R24) / 4;
//gv = (G12 + G32) / 2 + (R22 * 2 - R02 -R42) / 4;
//ge = (gh + gv) / 2;
short hd = ABS_DEC(block[2][1], block[2][3]) + ABS_DEC(block[2][2] * 2, block[2][0] + block[2][4]);
short vd = ABS_DEC(block[1][2], block[3][2]) + ABS_DEC(block[2][2] * 2, block[0][2] + block[4][2]);
short gh = (block[2][1] + block[2][3]) / 2 + (block[2][2] * 2 - block[2][0] - block[2][4]) / 4;
short gv = (block[1][2] + block[3][2]) / 2 + (block[2][2] * 2 - block[0][2] - block[4][2]) / 4;
short ge = (gh + gv) / 2;
if (hd < vd) {
return gh;
} else if (vd < hd) {
return gv;
} else {
return ge;
}
}
(
这里,ABS_DEC是自定义的基础宏拟函数。
#define ABS_DEC(a, b) (((a) > (b)) ? ((a) - (b)) : ((b) - (a)))
)
在一个5X5的窗口内,计算R或者B为中心时,G的优选值。
2)kernel_G_RB_h
short kernel_G_RB_h(u8 block[5][5])
{
/*
0 1 2 3 4
G B G B G 0
R G R G R 1
G B G B G 2
R G R G R 3
G B G B G 4
*/
short val = (block[2][1] + block[2][3]) / 2;
return val;
}
在一个5X5的窗口内,计算G为中心时,R或者B的水平值。
3)kernel_G_RB_v
short kernel_G_RB_v(u8 block[5][5])
{
/*
0 1 2 3 4
G B G B G 0
R G R G R 1
G B G B G 2
R G R G R 3
G B G B G 4
*/
short val = (block[1][2] + block[3][2]) / 2;
return val;
}
在一个5X5的窗口内,计算G为中心时,R或者B的垂直值。
4)kernel_RB_BR
short kernel_RB_BR(u8 block[5][5])
{
/*
0 1 2 3 4
R G R G R 0
G B G B G 1
R G R G R 2
G B G B G 3
R G R G R 4
*/
short hd = ABS_DEC(block[1][1], block[3][3]);
short vd = ABS_DEC(block[1][3], block[3][1]);
short ph = (block[1][1] + block[3][3]) / 2;
short pv = (block[1][3] + block[3][1]) / 2;
short pe = (ph + pv) / 2;
if (hd < vd) {
return ph;
} else if (vd < hd) {
return pv;
} else {
return pe;
}
}
在一个5X5的窗口内,计算R或者B为中心时,G的优选值。
++++++++++++++++++++++++++++++++++++++++++++++++++++++
然后,我们需要一个色彩转换函数。
template<int SRC_BPP, int NPPC, int DST_BPP>
T_UINT(DST_BPP, NPPC) bayer_inp_pixel(u8 block[5][5], u16 j, u16 i, u8 mode)
{
short raw = block[2][2];
short RB_G = kernel_RB_G(block);
short G_RB_h = kernel_G_RB_h(block);
short G_RB_v = kernel_G_RB_v(block);
short RB_BR = kernel_RB_BR(block);
short r0 = raw;
short g0 = RB_G;
short b0 = RB_BR;
short r1 = RB_BR;
short g1 = RB_G;
short b1 = raw;
short r2 = G_RB_v;
short g2 = raw;
short b2 = G_RB_h;
short r3 = G_RB_h;
short g3 = raw;
short b3 = G_RB_v;
short r = 0;
short g = 0;
short b = 0;
if (mode == 0)//BG
{
if ((i & 0x1) == 0) {
if ((j & 0x1) == 0) {
r = r1;
g = g1;
b = b1;
} else {
r = r2;
g = g2;
b = b2;
}
} else {
if ((j & 0x1) == 0) {
r = r3;
g = g3;
b = b3;
} else {
r = r0;
g = g0;
b = b0;
}
}
}
else if (mode == 1)//GB
{
if ((i & 0x1) == 0) {
if ((j & 0x1) == 0) {
r = r2;
g = g2;
b = b2;
} else {
r = r1;
g = g1;
b = b1;
}
} else {
if ((j & 0x1) == 0) {
r = r0;
g = g0;
b = b0;
} else {
r = r3;
g = g3;
b = b3;
}
}
}
else if (mode == 2)//GR
{
if ((i & 0x1) == 0) {
if ((j & 0x1) == 0) {
r = r3;
g = g3;
b = b3;
} else {
r = r0;
g = g0;
b = b0;
}
} else {
if ((j & 0x1) == 0) {
r = r1;
g = g1;
b = b1;
} else {
r = r2;
g = g2;
b = b2;
}
}
}
else //RG
{
if ((i & 0x1) == 0) {
if ((j & 0x1) == 0) {
r = r0;
g = g0;
b = b0;
} else {
r = r3;
g = g3;
b = b3;
}
} else {
if ((j & 0x1) == 0) {
r = r2;
g = g2;
b = b2;
} else {
r = r1;
g = g1;
b = b1;
}
}
}
r = (r > 255) ? 255 : ((r < 0) ? 0 : r);
g = (g > 255) ? 255 : ((g < 0) ? 0 : g);
b = (b > 255) ? 255 : ((b < 0) ? 0 : b);
T_UINT(DST_BPP, NPPC) dstpixel;
dstpixel(7, 0) = b;
dstpixel(15, 8) = g;
dstpixel(23, 16) = r;
return dstpixel;
}
该函数根据当前的window,以及提供的mode控制码,计算出window的中心像素的RGB值。
提供的i和j,指出对应的中心点的坐标。
+++++++++++++++++++++++++++++++++++++++++++
然后,我们需要实现功能函数。
template<int SRC_BPP, int ROWS, int COLS, int NPPC, int DST_BPP>
void bayer_to_rgb(hls::stream<T_UINT(SRC_BPP, NPPC)>& src, hls::stream<T_UINT(DST_BPP, NPPC)>& dst, u8 mode, u16 width, u16 height)
{
assert(((NPPC==1)) && "Only 1 pixel-parallelism are supported");
u16 rows = height;
u16 cols = width;
const int KN = 5;
u8 window[KN][KN];
u8 line_buffer[KN - 1][COLS / NPPC];
#pragma HLS array_partition variable=line_buffer complete dim=1
u16 lineStore = 3;
ap_uint<3> line0 = 3, line1 = 0, line2 = 1, line3 = 2;
PRE_ROWS_LOOP:
for (u16 i = 0; i < 2; i++)
{
#pragma HLS loop_tripcount avg = 2 max = 2
PRE_COLS_LOOP:
for (u16 j = 0; j < cols; j++)
{
#pragma HLS loop_tripcount avg = COLS max = COLS
#pragma HLS pipeline II = 1
line_buffer[i][j] = 0;
line_buffer[i + 2][j] = (u8)src.read();
}
}
ROWS_LOOP:
for (u16 i = 0; i < rows; i++)
{
#pragma HLS loop_tripcount avg = ROWS max = ROWS
#pragma HLS loop_flatten off
lineStore++;
if (lineStore > 3) {
lineStore = 0;
}
if (line0 == 0)
{
line0 = 1;
line1 = 2;
line2 = 3;
line3 = 0;
}
else if (line0 == 1)
{
line0 = 2;
line1 = 3;
line2 = 0;
line3 = 1;
}
else if (line0 == 2)
{
line0 = 3;
line1 = 0;
line2 = 1;
line3 = 2;
}
else
{
line0 = 0;
line1 = 1;
line2 = 2;
line3 = 3;
}
COLS_LOOP:
for (u16 j = 0; j < cols + KN/2; j++)
{
#pragma HLS loop_tripcount avg = COLS max = COLS
#pragma HLS pipeline II = 1
T_UINT(SRC_BPP, NPPC) srcpixel;
if ((i < (rows - KN/2)) && (j < cols))
{
src >> srcpixel;
}
for (u16 k = 0; k < KN; k++)
{
window[k][0] = window[k][1];
window[k][1] = window[k][2];
window[k][2] = window[k][3];
window[k][3] = window[k][4];
}
if (j < cols)
{
window[0][4] = line_buffer[line0][j];
window[1][4] = line_buffer[line1][j];
window[2][4] = line_buffer[line2][j];
window[3][4] = line_buffer[line3][j];
window[4][4] = (u8)srcpixel;
line_buffer[lineStore][j] = (u8)srcpixel;
}
if (j >= KN/2)
{
u16 dx = j - KN/2;
u16 dy = i;
T_UINT(DST_BPP, NPPC) dstpixel = bayer_inp_pixel<SRC_BPP, NPPC, DST_BPP>(window, dx, dy, mode);
dst << dstpixel;
}
}
}
}
函数中,定义了局部变量linebuffer,作为中间寄存器。这里,linebuffer是一个二维数组,可以存储4行。
代码分为几个大块。
首先,用一个两层嵌套for循环体,逐单元处理,将前两行读入到linebuffer的对应位置。
然后,再用一个三层嵌套for循环体,逐单元处理。
在第一层,
首先是进行各个控制变量的更新。例如 lineStore,line0,line1, line2, line3,
然后是进入第二层,处理本行内的各列的数据,
在第二层,
首先是读取当前列的数据,注意,这里用if执行块来进行读取保护,由于之前已经预读取两行,所以最后两行,是不从src中读取的。
然后是用一个for循环体和一个if执行块,构成一个代码块,更新windowbuffer。用代码块来描述window shift moving操作。
然后是调用上述的窗口计算函数,求出当前window的中心点的RGB值。
然后是将RGB像素值,输出到stream。
+++++++++++++++++++++++++++++++++++++++++++++++++++
补充:
window shift moving
widow shift moving,需要linebuffer的配合。
所以用代码块来描述window shift moving时,
一方面,需要描述linebuffer的 shift moving过程,
另一方面,需要描述window的shift moving过程。
对于current line current column, 需要存储两份,一份在window中,另一份在linebuffer中。
当一行读取结束后,则在linebuffer中完全存储完毕,在下一行开始的时候,之前存储的行,就可以做为linebuffer中最近的一行来使用。
存储像素到lnebuffer中,如代码所示:
...
src >> srcpixel;
...
line_buffer[lineStore][j] = (u8)srcpixel;
...
对于window shift moving的过程的描述,需要for循环体来描述这个过程。
(
这里约定,对于window,index号越大,代表数据越新。
window的第一维,代表行方向,第二维,代表列方向。
)
在描述window shift moving的过程时,
首先,描述数据依次复制移动的过程,这个顺序很重要,要保证“复制覆盖时,先旧后新,先覆盖较旧的数据,再覆盖较新的数据”。最终的效果是,最先覆盖的,是最旧的数据,最后一个位置,是冗余数据,即腾空的位置。可以被最新的数据覆盖,填充。
如代码所示:
...
for (u16 k = 0; k < KN; k++)
{
window[k][0] = window[k][1];
window[k][1] = window[k][2];
window[k][2] = window[k][3];
window[k][3] = window[k][4];
}
if (j < cols)
{
window[0][4] = line_buffer[line0][j];
window[1][4] = line_buffer[line1][j];
window[2][4] = line_buffer[line2][j];
window[3][4] = line_buffer[line3][j];
window[4][4] = (u8)srcpixel;
line_buffer[lineStore][j] = (u8)srcpixel;
}
...
先进行循环覆盖后,最后的腾空的位置,则从linebuffer中读取需要的位置的数据来填充。
在涉及到window的操作时,尤其要注意边界处理。
要注意进行读取保护,防止出现读取越界。
如代码所示:
...
if ((i < (rows - KN/2)) && (j < cols))
{
src >> srcpixel;
}
....
由于之前已经预读取两行到linebuffer中,
当读取到最后两行时,stream已经没有数据,
所以这里进行读取保护,
如果行计数器到了最后两行,则不读取。
在行计数器的最后两行,反复存入linebuffer的srcpixel数据,是最后读取得数据的冗余数据,因为srcpixel一直未被更新。
同时,这里也要进行写入保护,防止出现写入越界。
如代码所示:
...
for (u16 j = 0; j < cols + KN/2; j++)
{
...
if (j >= KN/2)
{
u16 dx = j - KN/2;
u16 dy = i;
T_UINT(DST_BPP, NPPC) dstpixel = bayer_inp_pixel<SRC_BPP, NPPC, DST_BPP>(window, dx, dy, mode);
dst << dstpixel;
}
...
}
...
在进行列计数时,额外多加了两个列号。
所以在列号小于2时,不会有写入操作。