Vivado HLS实现MNIST手写数字识别(2)

上一节介绍了如何使用搭建好的卷积和池化模块实现对SD卡中存储的手写数字的图像进行识别,这一节将介绍如何对摄像头采集到的图像数据进行手写数字的识别。

一、HLS图像处理部分

        卷积和池化模块的输入是28*28的单通道灰度图,而ov5640摄像头采集到的是1024*768的三通道彩色图。这里首先对摄像头采集到的画面进行裁剪,考虑到实际手写数字在采集图像中的位置,截取位于图像中心112*112大小的图片;接着对图像进行灰度化和二值化的操作,使得数据格式与训练输入相符合;再对图片大小进行缩放,将112*112大小的灰度图缩放到28*28大小。

void doKernel(AXI_STREAM & inStream,AXI_STREAM & outStream,unsigned char result[],int num)
{
#pragma HLS INTERFACE m_axi depth=4294967295 port=result offset=slave
#pragma HLS INTERFACE s_axilite port=return bundle=CTRL_BUS
#pragma HLS INTERFACE axis register both port=outStream
#pragma HLS INTERFACE axis register both port=inStream
#pragma HLS INTERFACE s_axilite port=num

	unsigned char tmp,cnt;
	unsigned char pix;
	unsigned char valOut_m=255;
	unsigned char valOut_z=0;
	RGB_image image1(4*MINST_WIDTH,4*MINST_WIDTH);
	Resize_image image2(MINST_WIDTH,MINST_WIDTH);
	Gray_image gray(MINST_WIDTH,MINST_WIDTH);

	uint_side_channel dataOutChannel;
	uint_side_channel currentChannel;


	for(int i = 0; i < (IMG_HEIGHT); i++ )
	{
		for(int j = 0; j < (IMG_WIDTH); j++)
		{
#pragma HLS PIPELINE
			currentChannel = inStream.read();
			hls::Scalar<3, unsigned char> pixel_val;
			if((i==(IMG_HEIGHT/2-2*MINST_WIDTH-1) || i==(IMG_HEIGHT/2+2*MINST_WIDTH)) && (j>=(IMG_WIDTH/2-2*MINST_WIDTH-1) && j<=(IMG_WIDTH/2+2*MINST_WIDTH)))
			{
				dataOutChannel.data = (valOut_m << 16) | (valOut_z << 8) | valOut_z;
			}
			else if((i>=(IMG_HEIGHT/2-2*MINST_WIDTH-1) && i<=(IMG_HEIGHT/2+2*MINST_WIDTH)) && (j==(IMG_WIDTH/2-2*MINST_WIDTH-1) || j==(IMG_WIDTH/2+2*MINST_WIDTH)))
			{
				dataOutChannel.data = (valOut_m << 16) | (valOut_z << 8) | valOut_z;
			}
			else
			{
				dataOutChannel.data = currentChannel.data;
			}
			if((i>=(IMG_HEIGHT/2-2*MINST_WIDTH) && i<=(IMG_HEIGHT/2+2*MINST_WIDTH-1)) && (j>=(IMG_WIDTH/2-2*MINST_WIDTH) && j<=(IMG_WIDTH/2+2*MINST_WIDTH-1)))
			{
				unsigned char R = currentChannel.data & 0xFF;
				unsigned char G = (currentChannel.data >> 8) & 0xFF;
				unsigned char B = (currentChannel.data >> 16) & 0xFF;
				pix = (R*76 + G*150 + B*30) >> 8;
				if(pix<16)
					pix=0;
				else if(pix<128)
					pix=pix>>3;
				else
					pix=255;
				dataOutChannel.data = (pix << 16) | (pix << 8) | pix;
				pixel_val.val[0] = pix;
				pixel_val.val[1] = pix;
				pixel_val.val[2] = pix;
				image1.write(pixel_val);
			}
			if(num<10){
				tmp = number[num][i][j/8];
				if(i<128 && (j-959)>0){
					if(tmp!=0){
						for(cnt=0;cnt<j%8;cnt++)
							tmp = tmp/2;
						if(tmp%2==1){
							dataOutChannel.data = (valOut_m << 16) | (valOut_z << 8) | valOut_z;
						}
					}
				}
			}

			dataOutChannel.dest = currentChannel.dest;
			dataOutChannel.id = currentChannel.id;
			dataOutChannel.keep = currentChannel.keep;
			dataOutChannel.strb = currentChannel.strb;
			dataOutChannel.user = currentChannel.user;
			dataOutChannel.last = currentChannel.last;
			outStream.write(dataOutChannel);
		}
	}
#pragma HLS dataflow
	hls::Resize(image1,image2,1);
	hls::CvtColor<HLS_BGR2GRAY>(image2,gray);
	hls::Mat2Array<MINST_WIDTH>(gray,result);
}

除了图像处理之外,这里还将识别出来的数字显示在屏幕上。同时为了提取图像处理后的结果,将result数组配置为m_axi类型。

二、Vivado系统搭建

这里将图像处理模块的输入连接摄像头模块的输出,图像处理模块的输出连接HDMI显示的输入。

三、Visit SDK设计

SDK的设计先启动初始化摄像头、图像处理模块以及卷积池化模块,接着初始化SD卡,读取卷积池化层的权值参数,最后将图像采集的结果输入到卷积池化模块输出结果。

#include <stdio.h>
#include "platform.h"
#include <stdlib.h>
#include <string.h>
#include "xil_types.h"
#include "xil_cache.h"
#include "xparameters.h"
#include "xaxivdma.h"
#include "xaxivdma_i.h"
#include "vdma_api/vdma_api.h"
#include "emio_sccb_cfg/emio_sccb_cfg.h"
#include "ov5640/ov5640_init.h"
#include "xdokernel.h"
#include "xil_printf.h"
#include "xil_cache.h"
#include "Convolution.h"
#include "Pool.h"
#include "sd.h"

#define HLS_VDMA_DEV_ID 		XPAR_AXI_VDMA_0_DEVICE_ID
#define DISP_VDMA_DEV_ID 		XPAR_AXI_VDMA_1_DEVICE_ID
#define DOKERNEL_DEV_ID 		XPAR_DOKERNEL_0_DEVICE_ID

#define HLS_BASE_ADDR			0x08000000
#define DISP_BASE_ADDR			0x03000000
#define SCREEN_X				1024
#define SCREEN_Y				768

static XAxiVdma     vdma;
static XDokernel dokernel;
static XDokernel_Config *dokernel_Cfg;

//Weight of Conv1
unsigned char orional_pic[28][28];
float image[28][28][1];
float W_conv1[3][3][1][12];
float b_conv1[12];
float h_conv1[28][28][12];
float h_pool1[14][14][12];

//Weight of Conv2
float W_conv2[3][3][12][24];
float b_conv2[24];
float h_conv2[14][14][24];
float h_pool2[7][7][24];

//Weight of FC1
float W_fc1[7*7*24][96];
float b_fc1[96];
float h_fc1[96];

//Weight of FC2
float W_fc2[96][10];
float b_fc2[10];
float h_fc2[10];

int main()
{
    init_platform();
    Xil_DCacheDisable();

    int i,j;

	u32 status;

	u16 cmos_h_pixel;
	u16 cmos_v_pixel;
	u16 total_h_pixel;
	u16 total_v_pixel;

	cmos_h_pixel = 1024;
	cmos_v_pixel = 768;
	total_h_pixel = 2570;
	total_v_pixel = 980;

	emio_init();

	status = ov5640_init( cmos_h_pixel,
						  cmos_v_pixel,
						 total_h_pixel,
						 total_v_pixel);
	if(status == 0)
		xil_printf("OV5640 detected successful!\r\n");
	else
		xil_printf("OV5640 detected failed!\r\n");

	dokernel_Cfg = XDokernel_LookupConfig(DOKERNEL_DEV_ID);
	status = XDokernel_CfgInitialize(&dokernel,dokernel_Cfg);
	if(status != XST_SUCCESS){
		printf("dokernel initialize failed! \n");
	}

    XConv xconv;
    if(XConv_Initialize(&xconv,XPAR_CONV_0_DEVICE_ID)!=XST_SUCCESS)
    	xil_printf("XConv device not found\r\n");

    XPool xpool;
    if(XPool_Initialize(&xpool,XPAR_POOL_0_DEVICE_ID)!=XST_SUCCESS)
    	xil_printf("XPool device not found\r\n");
    //初始化SD卡
    SD_Init();
    print("Hello World\r\n");

    LoadWeight("W_conv1.bin",3*3*1*12,W_conv1[0][0][0]);
    LoadWeight("b_conv1.bin",12,b_conv1);

    LoadWeight("W_conv2.bin",3*3*12*24,W_conv2[0][0][0]);
    LoadWeight("b_conv2.bin",24,b_conv2);

    LoadWeight("W_fc1.bin",7*7*24*96,W_fc1[0]);
    LoadWeight("b_fc1.bin",96,b_fc1);

    LoadWeight("W_fc2.bin",96*10,W_fc2[0]);
    LoadWeight("b_fc2.bin",10,b_fc2);

	run_vdma_frame_buffer(&vdma, DISP_VDMA_DEV_ID, SCREEN_X, SCREEN_Y,
			DISP_BASE_ADDR,0,0,BOTH);
	run_vdma_frame_buffer(&vdma, HLS_VDMA_DEV_ID, SCREEN_X, SCREEN_Y,
			HLS_BASE_ADDR,0,0,BOTH);

    while(1)
    {
    	XDokernel_Set_result(&dokernel,(unsigned int)orional_pic);
    	XDokernel_Start(&dokernel);
    	while(!XDokernel_IsDone(&dokernel));
    	for(i=0;i<28;i++){
    		for(j=0;j<28;j++){
    			image[i][j][0] = 1-orional_pic[i][j]/255.0;
    		}
    	}

		//Conv1
		RunConv(&xconv,1,28,28,12,//CHin,Hin,Win,CHout
				3,3,1,1,1,1,//Kx,Ky,Sx,Sy,mode,relu_en
				image[0][0],W_conv1[0][0][0],b_conv1,h_conv1[0][0]);//feature_in,W,bias,feature_out
		RunPool(&xpool,12,28,28,//CHin,Hin,Win
				2,2,2,//Kx,Ky,mode
				h_conv1[0][0],h_pool1[0][0]);//feature_in,feature_out

		//Conv2
		RunConv(&xconv,12,14,14,24,//CHin,Hin,Win,CHout
				3,3,1,1,1,1,//Kx,Ky,Sx,Sy,mode,relu_en
				h_pool1[0][0],W_conv2[0][0][0],b_conv2,h_conv2[0][0]);//feature_in,W,bias,feature_out
		RunPool(&xpool,24,14,14,//CHin,Hin,Win
				2,2,2,//Kx,Ky,mode
				h_conv2[0][0],h_pool2[0][0]);//feature_in,feature_out

		//FC1
		RunConv(&xconv,24,7,7,96,//CHin,Hin,Win,CHout
				7,7,1,1,0,1,//Kx,Ky,Sx,Sy,mode,relu_en
				h_pool2[0][0],W_fc1[0],b_fc1,h_fc1);//feature_in,W,bias,feature_out

		//FC2
		RunConv(&xconv,96,1,1,10,//CHin,Hin,Win,CHout
				1,1,1,1,0,1,//Kx,Ky,Sx,Sy,mode,relu_en
				h_fc1,W_fc2[0],b_fc2,h_fc2);//feature_in,W,bias,feature_out
		//相当于softmax的效果
		float max=-10000;int num=0;
		for(int m=0;m<10;m++)
		{
			if(h_fc2[m]>max)
			{
				max=h_fc2[m];
				num=m;
			}
		}
		xil_printf("predicted=%d  \r\n",num);
    }

    cleanup_platform();
    return 0;
}

演示效果:b站

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值