《DNK210使用指南 -SDK版 V1.0》第三十七章 车牌号识别实验

第三十七章 车牌号识别实验


       在上一章节中,介绍了利用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[7];
image_t kpu_image,crop_image,ai_image;
obj_info_t cla_obj_coord;
static float g_anchor[ANCHOR_NUM * 2] = {8.30891522166988, 2.75630994889035, 5.18609903718768, 1.7863757404970702, 6.91480529053198, 3.825771881004435, 10.218567655549439, 3.69476690620971, 6.4088204258368195, 2.38813526350986}; /*车牌号检测*/
 
char *province_cn[31] =
{
    "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[34] =
{
   "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[34];
    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[0] = max_index(out1, 31);
 
    features += chunk;
    kpu_fully_connected(features, weight2, bias2, out1, chunk, 24);
    index[1] = max_index(out1, 24);
 
    features += chunk;
    kpu_fully_connected(features, weight3, bias3, out1, chunk, 34);
    index[2] = max_index(out1, 34);
 
    features += chunk;
    kpu_fully_connected(features, weight4, bias4, out1, chunk, 34);
    index[3] = max_index(out1, 34);
 
    features += chunk;
    kpu_fully_connected(features, weight5, bias5, out1, chunk, 34);
    index[4] = max_index(out1, 34);
 
    features += chunk;
    kpu_fully_connected(features, weight6, bias6, out1, chunk, 34);
    index[5] = max_index(out1, 34);
 
    features += chunk;
    kpu_fully_connected(features, weight7, bias7, out1, chunk, 34);
    index[6] = 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[15];
    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[j].x1 >= 2)
                {
                    x1 = cla_obj_coord.obj[j].x1 - 2;   /* 对识别框稍微放大点 */
                }
                if (cla_obj_coord.obj[j].y1 >= 2)
                {
                    y1 = cla_obj_coord.obj[j].y1 - 2;
                }
                x2 = cla_obj_coord.obj[j].x2 + 2;
                y2 = cla_obj_coord.obj[j].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[lp_index_list[0]], ads[lp_index_list[1]], ads[lp_index_list[2]], 
                    ads[lp_index_list[3]], ads[lp_index_list[4]], ads[lp_index_list[5]], ads[lp_index_list[6]]);
                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显示车牌识别实验结果


请使用浏览器的分享功能分享到微信等