《DNK210使用指南 -SDK版 V1.0》第三十八章 MNIST实验

第三十八章 MNIST实验


       在上一章节中,介绍了利用KPU模块实现车牌号识别功能,本章将继续介绍利用KPU模块实现MNIST识别功能。通过本章的学习,读者将学习到使用SDK编程技术实现MNIST识别应用。

       本章分为如下几个小节:

       38.1 KPU模块介绍

       38.2 硬件设计

       38.3 程序设计

       38.4 运行验证


        38.1 KPU模块介绍

       有关KPU模块的介绍,请见第30.1小节《KPU介绍》。


        38.2 硬件设计


       38.2.1 例程功能


       1. 获取摄像头输出的图像,对图像进行预处理后,送入KPU进行MNIST手写数字识别,然后在LCD上显示识别出的数字和其对应的得分。


       38.2.2 硬件资源

       本章实验内容,主要讲解KPU模块的使用,无需关注硬件资源。


       38.2.3 原理图

       本章实验内容,主要讲解KPU模块的使用,无需关注原理图。


        38.3 程序设计


       38.3.1 main.c代码

       main.c中的代码如下所示:

INCBIN(model, "uint8_mnist_cnn_model.kmodel");
 
static volatile uint8_t ai_done_flag;
image_t kpu_image,ai_image,crop_image;
 
/* KPU运算完成回调 */
static void ai_done_callback(void *userdata)
{
    ai_done_flag = 1;
}
 
int main(void)
{
    kpu_model_context_t task;
    uint8_t *disp;
    uint8_t *ai;
    float *output;
    size_t output_size;
    char datatemp[20];
 
    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 = 112;
    ai_image.height = 112;
    image_init(&ai_image);
 
    crop_image.pixel = 3;
    crop_image.width = 224;
    crop_image.height = 224;
    image_init(&crop_image);
 
    if (kpu_load_kmodel(&task, (const uint8_t *)model_data) != 0)
    {
        printf("Kmodel load failed!\n");
        while (1);
    }
    
    while (1)
    {
        if (camera_snapshot(&disp, &ai) == 0)
        {
            kpu_image.addr = ai;
            image_crop(&kpu_image, &crop_image, (320 - 224) / 2 - 1,  (240 - 224) / 2 - 1);
            image_rgb888_to_gray(crop_image.addr, 224, 224);
            image_resize(&crop_image,&ai_image);
            image_invert(ai_image.addr, 112, 112);
            image_strech_chart(ai_image.addr, 112, 112, 1);
            
            ai_done_flag = 0;
            if (kpu_run_kmodel(&task, (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, 0, (uint8_t **)&output, &output_size) != 0)
            {
                printf("Output get failed!\n");
                while (1);
            }
            
            uint8_t cls = max_index(output, 10);
 
            sprintf((char *)datatemp,"number:%d",cls);
            draw_string_rgb565_image((uint16_t *)disp, 320, 240, 0, 0, datatemp, RED);
            draw_box_rgb565_image((uint16_t *)disp, 320, (320 - 224) / 2 - 1, (240 - 224) / 2 - 1, (320 - 224) / 2 + 224, (240 - 224) / 2 + 224,  GREEN);
            lcd_draw_picture(0, 0, 320, 240, (uint16_t *)disp);
            camera_snapshot_release();
        }
    }
}

       uint8_mnist_cnn_model.kmodel是数字识别模型,用于识别阿拉伯数字0~9,该模型网络运算的图片大小为112*112。

       可以看到一开始是先初始化了LCD和摄像头,初始化完成后创建两个大小分别为112*112和224*224的RGB888图片缓存区,一个用于网络模型运算,另一个用于图片转化,然后加载需要用到的网络模型,并初始化YOLO2网络。

       最后在一个循环中不断地获取摄像头输出的图像,图像尺寸为320*240,先将LCD显示器绿框内的图像切割出来,大小为224*224,然后通过image_rgb888_to_gray函数转换为灰度图,再将转换的灰度图缩小成112*112,此时还不满足我们模型的运算条件,我们还需要对灰度图的颜色值进行翻转,即对其取反,取反后用image_strech_chart函数消除黑边,再将其送入KPU中进行运算,然后再进行YOLO2网络运算,接着将KPU运算的结果作为输入传入max_index函数中获得相似度最高的值,最后将数字号绘制到LCD显示器。


        38.4 运行验证

       将DNK210开发板连接到电脑主机,通过VSCode将固件烧录到开发板中,将摄像头对手写数字,让其采集到手写数字图像,接着便可以看到LCD上显示了MNIST手写数字识别的结果,显示了识别出的数字结果,如下图所示:


图38.4.1 LCD显示MNIST识别实验结果


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