总体设计
该图像由下述命令生成,精度较高,可点开大图详细观看
write_bd_layout -force -format svg -verbose /tmp/block.design.svg
PL端:
- 读取ov5640摄像头采集的数据
- 使用Vitis HLS生成的IP核,将ov5640摄像头采集的rgb数据转为灰度数据,选定大小为112x112框,将框内灰度数据二值化,然后将灰度数据重新转为rgb数据,传向vdma
- rom_sel模块接收PS端传来的识别结果,根据识别结果选择对应的rom数据,传向data_handler
- data_handler模块接收rom数据,显示在屏幕左上角,并且将先前选定的识别框四周描上黑边,然后传给dvi_transmitter
- dvi_transmitter模块将rgb数据进行编码、并转串等操作,转为TMDS标准,进而通过HDMI口输出
PS端:
- 使用ps端spi控制ov5640摄像头
- 配置vdma
- 将vdma传到DDR的数据通过PS端编写的神经网络后得到10个输出, 分别代表0-9的概率,选取概率最大的作为识别结果, 将识别结果通过axi-lite传到PL端的rom_sel中
HLS图像处理具体实现
Vitis HLS代码中rgb数据转为灰度数据,并且在指定区域进行二值化代码如下:
template<int ROWS, int COLS>
void rgb2gray(xf::cv::Mat<XF_8UC3, ROWS, COLS, XF_NPPC1>&src,
xf::cv::Mat<XF_8UC1, ROWS, COLS, XF_NPPC1>&dst, ap_uint<8>threshold = 128)
{
for(int i = 0; i < ROWS; i++)
{
for(int j = 0; j < COLS; j++)
{
#pragma HLS PIPELINE
XF_TNAME(XF_8UC3, XF_NPPC1) pixel = src.read(i*COLS+j);
ap_uint<8> rgb[3];
extract_rgb(pixel, rgb);
ap_uint<8> gray = CalculateGRAY(rgb[0], rgb[1], rgb[2]);
if(i >= 184 && i <= 296 && j >= 320 && j <= 432)
{
gray = gray < threshold ? 255 : 0;
}
XF_TNAME(XF_8UC1,XF_NPPC1) gray_packed;
gray_packed.range(7, 0) = gray;
dst.write(i*COLS+j, gray_packed);
}
}
}
灰度数据转为rgb数据代码与之类似,不再赘述
模块总数据输入接口为axis流接口,输出接口为axis,并由vio控制ap_none类型的threshold变量的值
神经网络具体实现
1. 从DDR中读取数据,并将数据传入神经网络中
(注意:cmos_data的index需要乘3是因为cmos_data是rgb数据,实际我们只取了其中的一个通道作为灰度数据,这与我们对灰度转rgb的处理是相对应的)
// 初始化
unsigned int const frame_buffer_addr = (XPAR_PS7_DDR_0_S_AXI_BASEADDR
+ 0x1000000);
u8 *cmos_data = frame_buffer_addr;
// 读取数据
for(int row=0; row<VSIZE; row++)
{
for(int col=0; col<HSIZE; col++)
{
if(col>=320 && col<432 && row>=184 && row<296)
{
if(col%4 == 0 && row%4 == 0)
{
img_data[index] = cmos_data[(row*HSIZE+col)*3];
index++;
}
}
}
}
2. 神经网络各部分实现(以卷积层为例)
(为了保存中间数据,我们将数据保存到了ddr中的某个特定地址) 下面是卷积层的实现
// 初始化
float *cnn_param_w = 0x2500000;
float *cnn_param_b = 0x2500C00;
conv_param_init();
float *conv_rlst = 0x2500D00;
// 卷积层
for(int n=0; n<30; n++)
{
for(int row=0; row<=23; row++)
{
for(int col=0; col<=23; col++)
{
conv_temp = 0;
for(int x=0; x<5; x++)
{
for(int y=0; y<5; y++)
{
conv_temp += img_data[row*28+col+x*28+y] * cnn_param_w[x*5+y+n*25];
}
}
conv_temp += cnn_param_b[n];
// 激活函数
if(conv_temp > 0)
conv_rlst[row*24+col+n*24*24] = conv_temp;
else
conv_rlst[row*24+col+n*24*24] = 0;
}
}
}
3. 输出
for(int n=0; n<10; n++)
{
affine2_temp = 0;
for(int i=0; i<100;i++)
{
affine2_temp = affine2_temp + affine2_w[i+100*n] * affine1_rslt[i];
}
affine2_rslt[n] = affine2_temp;
// 选取最大值
if(temp <= affine2_rslt[n])
{
temp = affine2_rslt[n];
predict_num = n;
}
}
// 输出给axi-lite, 然后传给rom_sel
Xil_Out32(AXI_LITE_BASEADDR, predict_num);