本篇在查阅资源资料情况下,讲述将目标检测模型Yolo-FastestV2部署到GD32H759I-EVAL开发板上,实现AI目标识别。
GD32 AI ModelZoo是一个用于在GD32设备上快速部署AI模型的开源项目,其在github上发布: https://github.com/HomiKetalys/gd32ai-modelzoo。
一.了解AI模型与准备
在部署AI模型方面,GD32H759提供了一颗主频高达600Mhz的MCU,同时提供了64KB的L1 Cache,以及1M的片上RAM。Yolo-FastestV2是一个十分轻量的目标检测模型,其已在github上发布: https://github.com/dog-qiuqiu/Yolo-FastestV2 。对其了解可点这里。在GD32H759上运行一个目标检测模型并展示其检测结果,需要使用GD32H759I-EVAL开发板上的摄像头ov2640以及LCD,故硬件上按前面使用LCD、摄像头接好跳线帽。
二.代码准备
1.摄像头程序
(1)摄像头DCI驱动移植官方例程,并做修改。capture_mode将连续捕获模式改为快照捕获模式,以便采集图像可控。dci_config包含DCI的IO口配置、DCI参数配置、DCI DMA配置、使能开启。
/*!
\brief configure the DCI to interface with the camera module
\param[in] none
\param[out] none
\retval none
*/
void dci_config(void)
{
dci_parameter_struct dci_struct;
dma_single_data_parameter_struct dma_single_struct;
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_GPIOC);
rcu_periph_clock_enable(RCU_GPIOE);
rcu_periph_clock_enable(RCU_GPIOH);
rcu_periph_clock_enable(RCU_GPIOG);
rcu_periph_clock_enable(RCU_DCI);
/* DCI GPIO AF configuration */
/* configure DCI_PIXCLK(PE3), DCI_VSYNC(PB7), DCI_HSYNC(PA4) */
gpio_af_set(GPIOE, GPIO_AF_13, GPIO_PIN_3);
gpio_af_set(GPIOB, GPIO_AF_13, GPIO_PIN_7);
gpio_af_set(GPIOA, GPIO_AF_13, GPIO_PIN_4);
gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_3);
gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_3);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_7);
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_4);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_4);
/* configure DCI_D0(PC6), DCI_D1(PH10) DCI_D2(PC8), DCI_D3(PC9), DCI_D4(PE4), DCI_D5(PB6), DCI_D6(PE5), DCI_D7(PE6) */
gpio_af_set(GPIOC, GPIO_AF_13, GPIO_PIN_6);
gpio_af_set(GPIOC, GPIO_AF_13, GPIO_PIN_8);
gpio_af_set(GPIOC, GPIO_AF_13, GPIO_PIN_9);
gpio_af_set(GPIOB, GPIO_AF_13, GPIO_PIN_6);
gpio_af_set(GPIOE, GPIO_AF_13, GPIO_PIN_4);
gpio_af_set(GPIOE, GPIO_AF_13, GPIO_PIN_5);
gpio_af_set(GPIOE, GPIO_AF_13, GPIO_PIN_6);
gpio_af_set(GPIOH, GPIO_AF_13, GPIO_PIN_10);
gpio_mode_set(GPIOH, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_10);
gpio_output_options_set(GPIOH, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_10);
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_6);
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_9);
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_8);
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_6);
gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_4);
gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_4);
gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5);
gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_5);
gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_6);
gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_100_220MHZ, GPIO_PIN_6);
ckout0_init();
/* DCI configuration */
// dci_struct.capture_mode = DCI_CAPTURE_MODE_CONTINUOUS;
dci_struct.capture_mode = DCI_CAPTURE_MODE_SNAPSHOT;
dci_struct.clock_polarity = DCI_CK_POLARITY_RISING;
dci_struct.hsync_polarity = DCI_HSYNC_POLARITY_LOW;
dci_struct.vsync_polarity = DCI_VSYNC_POLARITY_LOW;
dci_struct.frame_rate = DCI_FRAME_RATE_ALL;
dci_struct.interface_format = DCI_INTERFACE_FORMAT_8BITS;
dci_init(&dci_struct);
nvic_irq_enable(DCI_IRQn, 2, 0); // 使能DCI NVIC中断
dci_interrupt_enable(DCI_INT_OVR); // 使能DCI FIFO溢出中断(非常重要!!!)
dci_interrupt_flag_clear(DCI_INT_OVR); // 清除溢出中断标志位
dci_interrupt_enable(DCI_INT_EF); // 帧结束中断使能,用以绘制一帧图像到LCD上并触发下一次捕获
dci_interrupt_flag_clear(DCI_INT_EF); // 清除帧结束中断标志位
/* DCI DMA configuration */
rcu_periph_clock_enable(RCU_DMA1);
rcu_periph_clock_enable(RCU_DMAMUX);
dma_single_data_para_struct_init(&dma_single_struct);
dma_deinit(DMA1, DMA_CH7);
dma_single_struct.request = DMA_REQUEST_DCI;
dma_single_struct.periph_addr = (uint32_t)DCI_DATA_ADDRESS;
dma_single_struct.memory0_addr = (uint32_t)0xC0000000;
dma_single_struct.direction = DMA_PERIPH_TO_MEMORY;
dma_single_struct.number = IMAGE_HEIGHT * IMAGE_WIDTH / 2;
dma_single_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_single_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_single_struct.periph_memory_width = DMA_PERIPH_WIDTH_32BIT;
dma_single_struct.circular_mode = DMA_CIRCULAR_MODE_ENABLE;
dma_single_struct.priority = DMA_PRIORITY_HIGH;
dma_single_data_mode_init(DMA1, DMA_CH7, &dma_single_struct);
dma_channel_enable(DMA1, DMA_CH7); // 使能DMA传输
dci_enable(); // 使能DCI
dci_capture_enable(); // 开启传输
}
(2)用ConfigDCI()来控制图像的采集。
void ConfigDCI()
{
dci_parameter_struct dci_struct;
dma_single_data_parameter_struct dma_single_struct;
// 使能RCC相关时钟
rcu_periph_clock_enable(RCU_DCI); // 使能DCI时钟
rcu_periph_clock_enable(RCU_DMA1); // 使能DMA1时钟
// DCI配置
dci_deinit(); // 复位DCI
/* DCI configuration */
// dci_struct.capture_mode = DCI_CAPTURE_MODE_CONTINUOUS;
dci_struct.capture_mode = DCI_CAPTURE_MODE_SNAPSHOT;
dci_struct.clock_polarity = DCI_CK_POLARITY_RISING;
dci_struct.hsync_polarity = DCI_HSYNC_POLARITY_LOW;
dci_struct.vsync_polarity = DCI_VSYNC_POLARITY_LOW;
dci_struct.frame_rate = DCI_FRAME_RATE_ALL;
dci_struct.interface_format = DCI_INTERFACE_FORMAT_8BITS;
dci_init(&dci_struct);
nvic_irq_enable(DCI_IRQn, 2, 0); // 使能DCI NVIC中断
dci_interrupt_enable(DCI_INT_OVR); // 使能DCI FIFO溢出中断(非常重要!!!)
dci_interrupt_flag_clear(DCI_INT_OVR); // 清除溢出中断标志位
dci_interrupt_enable(DCI_INT_EF); // 帧结束中断使能,用以绘制一帧图像到LCD上并触发下一次捕获
dci_interrupt_flag_clear(DCI_INT_EF); // 清除帧结束中断标志位
/* DCI DMA configuration */
rcu_periph_clock_enable(RCU_DMA1);
rcu_periph_clock_enable(RCU_DMAMUX);
dma_single_data_para_struct_init(&dma_single_struct);
dma_deinit(DMA1, DMA_CH7);
dma_single_struct.request = DMA_REQUEST_DCI;
dma_single_struct.periph_addr = (uint32_t)DCI_DATA_ADDRESS;
dma_single_struct.memory0_addr = (uint32_t)0xC0000000;
dma_single_struct.direction = DMA_PERIPH_TO_MEMORY;
dma_single_struct.number = IMAGE_HEIGHT * IMAGE_WIDTH / 2;
dma_single_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_single_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_single_struct.periph_memory_width = DMA_PERIPH_WIDTH_32BIT;
dma_single_struct.circular_mode = DMA_CIRCULAR_MODE_ENABLE;
dma_single_struct.priority = DMA_PRIORITY_HIGH;
dma_single_data_mode_init(DMA1, DMA_CH7, &dma_single_struct);
// dma_channel_subperipheral_select(DMA1, DMA_CH7, DMA_SUBPERI1); //绑定DMA通道与相应外设
// dma_flow_controller_config(DMA1, DMA_CH7, DMA_FLOW_CONTROLLER_DMA); //DMA作为传输控制器
dma_channel_enable(DMA1, DMA_CH7); // 使能DMA传输
dci_enable(); // 使能DCI
dci_capture_enable(); // 开启传输
}
这里配合模型参数,将摄像头像素长宽定义为256*256,并在如上配置中使用到。
#define IMAGE_WIDTH 256
#define IMAGE_HEIGHT 256
2.LCD驱动
LCD驱动初始化与官方例程基本一致。配置屏宽高 480*272。
/*********************************************************************************************************
* 函数名称:InitLCD
* 函数功能:初始化LCD模块
* 输入参数:void
* 输出参数:void
* 返 回 值:void
* 注 意:
*********************************************************************************************************/
void InitLCD(void)
{
u32 i;
// TLI初始化结构体
tli_parameter_struct tli_init_struct;
// 初始化LCD设备结构体(默认横屏,当前层为背景层)
g_structTLILCDDev.pWidth = LCD_PIXEL_WIDTH;
g_structTLILCDDev.pHeight = LCD_PIXEL_HEIGHT;
g_structTLILCDDev.dir = LCD_SCREEN_HORIZONTAL;
g_structTLILCDDev.currentLayer = LCD_LAYER_BACKGROUND;
g_structTLILCDDev.frameBuf = s_arrBackgroundFrame;
g_structTLILCDDev.backFrameAddr = BACK_FRAME_START_ADDR;
g_structTLILCDDev.foreFrameAddr = FORE_FRAME_START_ADDR;
g_structTLILCDDev.pixelSize = 2;
for (i = 0; i < LCD_LAYER_MAX; i++)
{
g_structTLILCDDev.width[i] = g_structTLILCDDev.pWidth;
g_structTLILCDDev.height[i] = g_structTLILCDDev.pHeight;
}
// 使能RCU时钟
rcu_periph_clock_enable(RCU_TLI);
tli_gpio_config();
// 复位TLI
tli_deinit();
tli_struct_para_init(&tli_init_struct);
// 配置TLI时钟(时钟频率为1MHz * 200 / 5 / 4 = 10MHz,数据手册给的典型值是30MHz,但是10MHz刷新率下更稳定)
// if(ERROR == rcu_pllsai_config(240, 4, 4)){while(1);}
// rcu_tli_clock_div_config(RCU_PLLSAIR_DIV2);
// // if(ERROR == rcu_pllsai_config(200, 4, 5)){while(1);}
// // rcu_tli_clock_div_config(RCU_PLLSAIR_DIV4);
// // if(ERROR == rcu_pllsai_config(50, 4, 5)){while(1);}
// // rcu_tli_clock_div_config(RCU_PLLSAIR_DIV4);
// rcu_osci_on(RCU_PLLSAI_CK);
// if(ERROR == rcu_osci_stab_wait(RCU_PLLSAI_CK)){while(1);}
rcu_pll_input_output_clock_range_config(IDX_PLL2, RCU_PLL2RNG_1M_2M, RCU_PLL2VCO_150M_420M);
if (ERROR == rcu_pll2_config(25, 188, 3, 3, 3))
{
while (1)
{
}
}
rcu_pll_clock_output_enable(RCU_PLL2R);
rcu_tli_clock_div_config(RCU_PLL2R_DIV8);
rcu_osci_on(RCU_PLL2_CK);
if (ERROR == rcu_osci_stab_wait(RCU_PLL2_CK))
{
while (1)
{
}
}
// TLI初始化
// 信号极性配置
tli_init_struct.signalpolarity_hs = TLI_HSYN_ACTLIVE_LOW; // 水平同步脉冲低电平有效
tli_init_struct.signalpolarity_vs = TLI_VSYN_ACTLIVE_LOW; // 垂直同步脉冲低电平有效
tli_init_struct.signalpolarity_de = TLI_DE_ACTLIVE_LOW; // 数据使能低电平有效
tli_init_struct.signalpolarity_pixelck = TLI_PIXEL_CLOCK_TLI; // 像素时钟是TLI时钟
// 显示时序配置
tli_init_struct.synpsz_hpsz = HORIZONTAL_SYNCHRONOUS_PULSE - 1; // 水平同步脉冲宽度
tli_init_struct.synpsz_vpsz = VERTICAL_SYNCHRONOUS_PULSE - 1; // 垂直同步脉冲宽度
tli_init_struct.backpsz_hbpsz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH - 1; // 水平后沿加同步脉冲的宽度
tli_init_struct.backpsz_vbpsz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH - 1; // 垂直后沿加同步脉冲的宽度
tli_init_struct.activesz_hasz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH + ACTIVE_WIDTH - 1; // 水平有效宽度加后沿像素和水平同步像素宽度
tli_init_struct.activesz_vasz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH + ACTIVE_HEIGHT - 1; // 垂直有效宽度加后沿像素和垂直同步像素宽度
tli_init_struct.totalsz_htsz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH + ACTIVE_WIDTH + HORIZONTAL_FRONT_PORCH - 1; // 显示器的水平总宽度
tli_init_struct.totalsz_vtsz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH + ACTIVE_HEIGHT + VERTICAL_FRONT_PORCH - 1; // 显示器的垂直总宽度
// 配置BG层颜色(黑色)
tli_init_struct.backcolor_red = 0x00;
tli_init_struct.backcolor_green = 0x00;
tli_init_struct.backcolor_blue = 0x00;
// 根据参数配置TLI
tli_init(&tli_init_struct);
// 初始化背景层和前景层
LCDLayerWindowSet(LCD_LAYER_BACKGROUND, 0, 0, LCD_PIXEL_WIDTH, LCD_PIXEL_HEIGHT);
LCDLayerWindowSet(LCD_LAYER_FOREGROUND, 0, 0, LCD_PIXEL_WIDTH, LCD_PIXEL_HEIGHT);
// 使能背景层
LCDLayerEnable(LCD_LAYER_BACKGROUND);
// 禁用前景层
LCDLayerDisable(LCD_LAYER_FOREGROUND);
// 设置背景层透明度
LCDTransparencySet(LCD_LAYER_BACKGROUND, 0xFF);
// 设置前景层透明度
LCDTransparencySet(LCD_LAYER_FOREGROUND, 0xFF);
// 开启抖动
tli_dither_config(TLI_DITHER_ENABLE);
// //关闭抖动
// tli_dither_config(TLI_DITHER_DISABLE);
// 立即更新TLI寄存器
tli_reload_config(TLI_REQUEST_RELOAD_EN);
// TLI外设使能
tli_enable();
// 切换到背景层
LCDLayerSwitch(LCD_LAYER_BACKGROUND);
// 横屏显示
LCDDisplayDir(LCD_SCREEN_HORIZONTAL);
// 清屏
LCDClear(LCD_COLOR_BLACK);
}
这里使用竖屏显示。LCD显示方向函数如下:
/*********************************************************************************************************
* 函数名称: LCDDisplayDir
* 函数功能: 设置LCD显示方向
* 输入参数: dir:显示方向(LCD_SCREEN_HORIZONTAL,LCD_SCREEN_VERTICAL)
* 输出参数: void
* 返 回 值: void
* 注 意:
*********************************************************************************************************/
void LCDDisplayDir(EnumTLILCDDir dir)
{
u32 swap;
// 交换宽度和高度
if (g_structTLILCDDev.dir != dir)
{
// 保存显示方向
g_structTLILCDDev.dir = dir;
// 交换背景层宽度和高度
swap = g_structTLILCDDev.width[LCD_LAYER_BACKGROUND];
g_structTLILCDDev.width[LCD_LAYER_BACKGROUND] = g_structTLILCDDev.height[LCD_LAYER_BACKGROUND];
g_structTLILCDDev.height[LCD_LAYER_BACKGROUND] = swap;
// 交换前景层宽度和高度
swap = g_structTLILCDDev.width[LCD_LAYER_FOREGROUND];
g_structTLILCDDev.width[LCD_LAYER_FOREGROUND] = g_structTLILCDDev.height[LCD_LAYER_FOREGROUND];
g_structTLILCDDev.height[LCD_LAYER_FOREGROUND] = swap;
}
}
3.在gd32h7xx_it.c实现了像素转换映射片外RAM地址LCD显示
4.模型Yolo-FastestV2在GD32H759I上进行部署。
进入对应模型网址 https://github.com/HomiKetalys/gd32ai-modelzoo ,克隆获取资源如下:
GD32AI-ModelZoo提供了该模型的训练,导出,以及近乎一键的部署。在COCO80上训练的模型中,提供了Yolo-FastestV2模型在COCO2017上训练的检测80种目标的模型。提供了两种分辨率192*192 和256*256,我们这里使用256*256.
工程如下:
5.边缘AI目标检测实现
这里采用帧获取轮询检测方式。获取一帧图片后,给到AI_Run函数处理,返回结果,包含识别物个数及种类。
在一次检测结果出来后,将结果在图中框出,并显示探测到的个数与识别物名称。detect_once函数如下。在检测完显示后开始下一帧传输。
//检测一次
void detect_once(void)
{
while(!((1 == get_frame_flag())));
u32 i,x, y,t0,t1,t2;
char str[128];
u16 *frame=(u16 *) frame_addr;
time_start();
t0=get_time_stamp();
AI_Run((u8 *)frame);
t1=get_time_stamp();
t0=t1-t0;
//将结果显示在屏幕
t1=get_time_stamp();
for(y = 24; y <= LCD_Y-1; y++)
{
for(x = 0; x <= LCD_X-1; x++)
{
if((x>=SHOW_X0)&&(x<=SHOW_X1)&&(y>=SHOW_Y0)&&(y<=SHOW_Y1))
LCDDrawPoint(x, y, frame[IMAGE_HEIGHT*IMAGE_WIDTH-1-(y-SHOW_Y0)*IMAGE_WIDTH-(x-SHOW_X0)]);
else
LCDDrawPoint(x, y, 0xffff);
}
}
#ifndef TEST_TIME
LCDColorFillPixel(0,0,272,24*7,LCD_COLOR_YELLOW);
sprintf(str,"detect object_num:%d",object_num);
LCDShowString(0,24*7,24*10-1,24,LCD_FONT_24,LCD_TEXT_TRANS,LCD_COLOR_MAGENTA,LCD_COLOR_CYAN,str);
for(i=0;i<object_num;i++)
{
LCDShowString(SHOW_X0+(u32)results[i].bbox.x_min,SHOW_Y0+(u32)results[i].bbox.y_min-16,480-1,16,LCD_FONT_16,LCD_TEXT_TRANS,0x07f0,0xffff,(char *) activities[results[i].cls_index]);
LCDDrawRectangle(SHOW_X0+(u32)results[i].bbox.x_min,SHOW_Y0+(u32)results[i].bbox.y_min,SHOW_X0+(u32)results[i].bbox.x_max,SHOW_Y0+(u32)results[i].bbox.y_max,0xfff0);
sprintf(str,"%s ",(char *) activities[results[i].cls_index]);
}
if(object_num>0)
{
LCDShowString(0,24*8,24*10-1,24,LCD_FONT_24,LCD_TEXT_TRANS,LCD_COLOR_RED,LCD_COLOR_CYAN,str);
}
else
{
sprintf(str,"none");
LCDShowString(0,24*8,24*10-1,24,LCD_FONT_24,LCD_TEXT_TRANS,LCD_COLOR_BLUE,LCD_COLOR_CYAN,str);
}
#endif
t2=get_time_stamp();
t1=t2-t1;
u32 sys=rcu_clock_freq_get(CK_SYS)/1000000;
u32 ahb=rcu_clock_freq_get(CK_AHB)/1000000;
u32 apb1=rcu_clock_freq_get(CK_APB1)/1000000;
u32 apb2=rcu_clock_freq_get(CK_APB2)/1000000;
LCDShowString(0,0,LCD_X-1,24,LCD_FONT_24,LCD_TEXT_NORMAL,LCD_COLOR_BLUE,LCD_COLOR_YELLOW,"results:");
sprintf(str,"infer_time:%dms",t0);
LCDShowString(0,24*1,24*20-1,24,LCD_FONT_24,LCD_TEXT_NORMAL,LCD_COLOR_BLACK,LCD_COLOR_YELLOW,str);
sprintf(str,"display_time:%dms",t1);
LCDShowString(0,24*2,24*20-1,24,LCD_FONT_24,LCD_TEXT_NORMAL,LCD_COLOR_BLACK,LCD_COLOR_YELLOW,str);
sprintf(str,"SYS:%dMHz",sys);
LCDShowString(0,24*3,24*10-1,24,LCD_FONT_24,LCD_TEXT_NORMAL,LCD_COLOR_BLACK,LCD_COLOR_YELLOW,str);
sprintf(str,"AHB:%dMHz",ahb);
LCDShowString(0,24*4,24*10-1,24,LCD_FONT_24,LCD_TEXT_NORMAL,LCD_COLOR_BLACK,LCD_COLOR_YELLOW,str);
sprintf(str,"APB1:%dMHz",apb1);
LCDShowString(0,24*5,24*10-1,24,LCD_FONT_24,LCD_TEXT_NORMAL,LCD_COLOR_BLACK,LCD_COLOR_YELLOW,str);
sprintf(str,"APB2:%dMHz",apb2);
LCDShowString(0,24*6,24*10-1,24,LCD_FONT_24,LCD_TEXT_NORMAL,LCD_COLOR_BLACK,LCD_COLOR_YELLOW,str);
//开启下一帧传输
clear_frame_flag();
ConfigDCI();
}
6.main函数如下
/*!
\brief main function
\param[in] none
\param[out] none
\retval none
*/
int main(void)
{
ov2640_id_struct ov2640id;
/* enable the CPU cache */
cache_enable();
systick_config();
usart_config();
/* SDRAM initialization */
exmc_synchronous_dynamic_ram_init(EXMC_SDRAM_DEVICE0);
delay_1ms(1000);
/* camera initialization */
dci_ov2640_init();
dci_ov2640_id_read(&ov2640id);
/* LCD configure and TLI enable */
InitLCD();
LCDDisplayDir(LCD_SCREEN_VERTICAL);
/* Timer Init */
timer_config();
/* AI Init */
AI_Init(IMAGE_WIDTH,IMAGE_HEIGHT,0);
LCDClear(0xffff);
void detect_once();
while(1)
{
detect_once();
}
}
三.测验
1.编译烧录后,在没有80种识别物摄像头下,可以看到LCD屏显示如下。这里分为256*256摄像头显示区,和上方黄色背景时间间隔、时钟频率展示区和中间粉色、蓝色/红色 两行检测数量、目标的展示区。
2.取一张照片如下图7,用开发板摄像头对准,可检测到结果如下图8
至此,借用网络资源加以理解运用,实现了兆易GD32H759I边缘AI目标识别功能,评估了其具备AI的能力。
附参考源及代码工程:
【1】http://www.360doc.com/content/24/0416/15/70238708_1120553573.shtml
【2】https://mp.weixin.qq.com/s/mi6TFCmWUw9HQ8TMb9djXA
本帖最后由 dirty 于 2024-6-22 14:33 编辑兆易GD32H759I-EVAL AI工程
600MHz,图片视频频率能达到多少帧?
能跑AI这开发板性能不错呀。这个做人脸识别,效果如何?
wangerxian 发表于 2024-6-23 12:04 600MHz,图片视频频率能达到多少帧?
帧率这应用没直接一直刷帧测,因做AI,获取一帧后AI处理,用时在360ms左右,然后显示结果用时在30ms左右,依此循环。
本帖最后由 dirty 于 2024-6-24 23:32 编辑引用: dirty 发表于 2024-6-24 09:27 帧率这应用没直接一直刷帧测,因做AI,获取一帧后AI护理,用时在360ms左右,然后显示结果用时在30ms左右, ...
那600MHz处理图像还是很吃力的。
引用: wangerxian 发表于 2024-6-24 15:20 那600MHz处理图像还是很吃力的。
毕竟不是带NPU的,上的也是轻量模型。这芯片能做边缘AI方面的,一般场景还是够用,相比较其他同类芯片而言,还是很不错了
引用: dirty 发表于 2024-6-24 23:35 毕竟不是带NPU的,上的也是轻量模型。这芯片能做边缘AI方面的,一般场景还是够用,相比较其他同类芯片而 ...
嗯嗯,能体验一下确实也不错。