正点原子官方 发表于 2025-11-12 09:41

【正点原子K210连载】第三十七章 车牌号识别实验《DNK210使用指南-SDK版》

第三十七章 车牌号识别实验 在上一章节中,介绍了利用KPU模块实现人脸识别功能,本章将继续介绍利用KPU模块实现车牌号识别功能。通过本章的学习,读者将学习到使用SDK编程技术实现车牌号识别应用。本章分为如下几个小节:37.1 KPU模块介绍37.2 硬件设计37.3 程序设计37.4 运行验证

37.1 KPU模块介绍有关KPU模块的介绍,请见第30.1小节《KPU介绍》。37.2 硬件设计37.2.1 例程功能1. 获取摄像头输出的图像,并送入KPU进行车牌检测,接着对检测到的车牌分别进行车牌号识别,然后在LCD上显示检测到的车牌位置和识别出的车牌号码。37.2.2 硬件资源本章实验内容,主要讲解KPU模块的使用,无需关注硬件资源。37.2.3 原理图本章实验内容,主要讲解KPU模块的使用,无需关注原理图。37.3 程序设计37.3.1 main.c代码main.c中的代码如下所示:INCBIN(model_kpu_detect, "lp_detect.kmodel");INCBIN(model_kpu_recog, "lp_recog.kmodel");INCBIN(weight, "lp_weight.bin"); uint32_t lp_index_list;image_t kpu_image,crop_image,ai_image;obj_info_t cla_obj_coord;static float g_anchor = {8.30891522166988, 2.75630994889035, 5.18609903718768, 1.7863757404970702, 6.91480529053198, 3.825771881004435, 10.218567655549439, 3.69476690620971, 6.4088204258368195, 2.38813526350986}; /*车牌号检测*/ char *province_cn ={    "Wan", "Hu", "Jin", "Yu", "Ji", "Sx", "Meng", "Liao", "Jl", "Hei", "Su", "Zhe", "Jing", "Min", "Gan", "Lu", "Yu", "E", "Xiang", "Yue", "Gui", "Qiong", "Cuan", "Gui", "Yun", "Zang", "Shan", "Gan", "Qing", "Ning", "Xin"};char *ads ={   "A","B","C","D","E","F","G","H","J","K","L","M","N","P","Q","R","S","T","U","V","W","X","Y","Z","0","1","2","3","4","5","6","7","8","9"};static volatile uint8_t ai_done_flag; void lp_recog_process(const float *features, size_t features_size, uint32_t *index){    float out1;    size_t chunk = (features_size / sizeof(float)) / 7;    float *weight1 = (float *)weight_data;    float *bias1 = weight1 + 51584;    float *weight2 = bias1 + 31;    float *bias2 = weight2 + 39936;    float *weight3 = bias2+ 24;    float *bias3 = weight3 + 56576;    float *weight4 = bias3+ 34;    float *bias4 = weight4 + 56576;    float *weight5 = bias4+ 34;    float *bias5 = weight5 + 56576;    float *weight6 = bias5+ 34;    float *bias6 = weight6 + 56576;    float *weight7 = bias6+ 34;    float *bias7 = weight7 + 56576;     kpu_fully_connected(features, weight1, bias1, out1, chunk, 31);    index = max_index(out1, 31);     features += chunk;    kpu_fully_connected(features, weight2, bias2, out1, chunk, 24);    index = max_index(out1, 24);     features += chunk;    kpu_fully_connected(features, weight3, bias3, out1, chunk, 34);    index = max_index(out1, 34);     features += chunk;    kpu_fully_connected(features, weight4, bias4, out1, chunk, 34);    index = max_index(out1, 34);     features += chunk;    kpu_fully_connected(features, weight5, bias5, out1, chunk, 34);    index = max_index(out1, 34);     features += chunk;    kpu_fully_connected(features, weight6, bias6, out1, chunk, 34);    index = max_index(out1, 34);     features += chunk;    kpu_fully_connected(features, weight7, bias7, out1, chunk, 34);    index = max_index(out1, 34);} /* KPU运算完成回调 */static void ai_done_callback(void *userdata){    ai_done_flag = 1;} int main(void){    uint8_t *disp;    uint8_t *ai;    char show_lp_str;    kpu_model_context_t task_kpu;    kpu_model_context_t task_lp;    uint16_t x1 = 0,y1 = 0,x2,y2,cut_width, cut_height;    float *lp_box,*lp_number;    size_t lp_box_size,lp_number_size;    region_layer_t detect_kpu;     sysctl_pll_set_freq(SYSCTL_PLL0, 800000000);    sysctl_pll_set_freq(SYSCTL_PLL1, 400000000);    sysctl_pll_set_freq(SYSCTL_PLL2, 45158400);    sysctl_clock_enable(SYSCTL_CLOCK_AI);    sysctl_set_power_mode(SYSCTL_POWER_BANK6, SYSCTL_POWER_V18);    sysctl_set_power_mode(SYSCTL_POWER_BANK7, SYSCTL_POWER_V18);    sysctl_set_spi0_dvp_data(1);     lcd_init();    lcd_set_direction(DIR_YX_LRUD);    camera_init(24000000);    camera_set_pixformat(PIXFORMAT_RGB565);    camera_set_framesize(320, 240);     kpu_image.pixel = 3;    kpu_image.width = 320;    kpu_image.height = 240;    // image_init(&kpu_image);     ai_image.pixel = 3;    ai_image.width = 208;    ai_image.height = 64;    image_init(&ai_image);     if (kpu_load_kmodel(&task_kpu, (const uint8_t *)model_kpu_detect_data) != 0)    {      printf("Kmodel load failed!\n");      while (1);    }     if (kpu_load_kmodel(&task_lp, (const uint8_t *)model_kpu_recog_data) != 0)    {      printf("Kmodel load failed!\n");      while (1);    }     detect_kpu.anchor_number = ANCHOR_NUM;    detect_kpu.anchor = g_anchor;    detect_kpu.threshold = 0.7;    detect_kpu.nms_value = 0.3;    region_layer_init(&detect_kpu, 20, 15, 25, 320, 240);     while (1)    {      if (camera_snapshot(&disp, &ai) == 0)      {            ai_done_flag = 0;            if (kpu_run_kmodel(&task_kpu, (const uint8_t *)ai, DMAC_CHANNEL5, ai_done_callback, NULL) != 0)            {                printf("Kmodel run failed!\n");                while (1);            }            while (ai_done_flag == 0);             if (kpu_get_output(&task_kpu, 0, (uint8_t **)&lp_box, &lp_box_size) != 0)            {                printf("Output get failed!\n");                while (1);            }            detect_kpu.input = lp_box;            region_layer_run(&detect_kpu, &cla_obj_coord);             for (size_t j = 0; j < cla_obj_coord.obj_number; j++)                  {                if (cla_obj_coord.obj.x1 >= 2)                {                  x1 = cla_obj_coord.obj.x1 - 2;   /* 对识别框稍微放大点 */                }                if (cla_obj_coord.obj.y1 >= 2)                {                  y1 = cla_obj_coord.obj.y1 - 2;                }                x2 = cla_obj_coord.obj.x2 + 2;                y2 = cla_obj_coord.obj.y2 + 2;                                draw_box_rgb565_image((uint16_t *)disp, 320, x1, y1, x2, y2, GREEN);                cut_width = x2 - x1 ;                 cut_height = y2 - y1 ;                                 kpu_image.addr = ai;                crop_image.pixel = 3;                crop_image.width = cut_width;                crop_image.height = cut_height;                image_init(&crop_image);                image_crop(&kpu_image, &crop_image, x1 - 2, y1 - 2);                 image_resize(&crop_image,&ai_image);                image_deinit(&crop_image);                 image_replace(ai_image.addr, 208, 64, 0, 1);                ai_done_flag = 0;                if (kpu_run_kmodel(&task_lp, (const uint8_t *)ai_image.addr, DMAC_CHANNEL5, ai_done_callback, NULL) != 0)                {                  printf("Kmodel run failed!\n");                  while (1);                }                while (ai_done_flag == 0);                 if (kpu_get_output(&task_lp, 0, (uint8_t **)&lp_number, &lp_number_size) != 0)                {                  printf("Output get failed!\n");                  while (1);                }                lp_recog_process(lp_number, lp_number_size, lp_index_list);                 sprintf(show_lp_str,"%s %s-%s%s%s%s%s", province_cn], ads], ads],                   ads], ads], ads], ads]);                draw_string_rgb565_image((uint16_t *)disp, 320, 240, x1, y2 + 16, show_lp_str, RED);                // printf("%s\r\n",show_lp_str);            }            lcd_draw_picture(0, 0, 320, 240, (uint16_t *)disp);            camera_snapshot_release();      }    }}本实验使用了两个AI模型和一个KPU运算需要的bin文件。lp_detect.kmodel是车牌检测模型,用于获取车牌在图像中的坐标,网络运算的图片大小为320*240。lp_recog.kmodel是车牌号码识别的模型,用于提取车牌号的数据,网络运算的图片大小为208*64。可以看到一开始是先初始化了LCD和摄像头,初始化完成后创建一个208*64的RGB888图片缓存区,然后加载上述两个需要用到的AI模型,并初始化YOLO2网络,配置region_layer_t结构体参数的数据。最后在一个循环中不断地获取摄像头输出的图像,图像尺寸为320*240,将摄像头图像送入KPU中进行运算,然后将运算结果作为输入传入region_layer_run函数进行解析,该函数会把解析的车牌坐标放在cla_obj_coord结构体中,进而通过两个坐标点提取摄像头图像的车牌区域,我们将车牌区域切割下来,再缩放成208*64大小的图像,此时我们还需要将缩放后的图像用image_replace函数实现水平翻转(车牌号识别模型需要),最后将其送入KPU中进行运算,然后再进行YOLO2网络运算,将运算的结果传入lp_recog_process函数进行提取,并将运算结果绘制到LCD显示器上。37.4 运行验证将DNK210开发板连接到电脑主机,通过VSCode将固件烧录到开发板中,将摄像头对准车牌,让其采集到车牌图像,可以看到LCD上显示了车牌识别的结果,图像中的被检测到的车牌均被框出,并且显示了车牌对应识别出的车牌号码,如下图所示: 图37.4.1 LCD显示车牌识别实验结果
页: [1]
查看完整版本: 【正点原子K210连载】第三十七章 车牌号识别实验《DNK210使用指南-SDK版》