HLS第十课(bayer to rgb)

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时,不会有写入操作。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值