【聆思CSK6 视觉AI开发套件试用】利用文件系统实现人脸识别数据掉电保存,上电自动加载

本篇文章来自极术社区与聆思科技组织的CSK6 视觉AI开发套件活动,更多开发板试用活动请关注极术社区网站。作者:可乐跑枸杞

首先非常感谢聆思科技、极术社区提供了这次CSK6视觉AI开发套件的体验机会。本次体验包括以下几个部分:

  • 环境搭建
  • 体验人脸识别功能
  • 体验文件系统的功能
  • 针对重新上电后人脸数据丢失的问题,利用文件系统将特征数据存储在flash中,下次上电自动加载人脸数据。
  • 总结

1. 开发环境安装

1.1 win11搭建环境

https://docs.listenai.com/chips/600X/application/getting\_start

官网有详细的文档,在此不在赘述。

1.2 验证环境

lisa zep create

在命令行中进入刚创建的 hello\_world 项目目录,执行编译命令。

cd hello_worldlisa zep 

build -b csk6011a_nano

lisa zep flash

至此环境已经搭建成功!

2. 人脸识别应用源码概览

2.1 main文件的几个结构体

2.1.1 face\_register\_t

face_register_t结构体贯穿整个main文件,主要是负责保存设备实例,消息队列结构体,一张人脸的特征数据,特征数据的有效标志等

typedef struct {
    float feature[FD_MAX_FEATURE_DIMS];
    float feature_nvs[FD_MAX_FEATURE_DIMS];
    bool get_new_feature;
    fd_t *fd;
    bool is_usb_cfg;
    const struct device *video;
    struct video_format fmt;
    char result_txt[512];
    struct k_msgq msg;
} face_register_t;

2.1.2 storage_info_t

其中人脸数据存放在storage结构体中。storage结构体里维护了一个FIFO 队列,队列最大长度为10。

#define STORAGE_DATA_NUM_MAX    (10)

typedef struct {
    struct k_fifo data_fifo; 
    uint32_t data_cnt;
} storage_info_t;

2.2 main文件的几个函数

2.2.1 设置算法参数 — alago_init

int alago_init(void)
{
    float value = 0.4;
    fd_set_params(fd, FD_PARAM_FACE_DETECT_THRES, &value);  

    value = 30.0;
    fd_set_params(fd, FD_PARAM_FACE_ALIGN_YAWTHRES, &value);

    value = 30.0;
    fd_set_params(fd, FD_PARAM_FACE_ALIGN_PITCHTHRES, &value);

    value = 30.0;
    fd_set_params(fd, FD_PARAM_FACE_ALIGN_ROLLTHRES, &value); 

    value = 0.0;
    fd_set_params(fd, FD_PARAM_ANTI_SPOOFING_THRES, &value);   
}

当前视觉SDK,针对人脸识别,算法层面支持以下参数参数的配置:

参数type功能说明取值范围
FD_PARAM_FACE_DETECT_THRESfloat人脸检测框门限0~1
FD_PARAM_FACE_DETECT_PIXESIZEfloat检测框最小像素值门限0~640
FD_PARAM_FACE_ALIGN_YAWTHRESfloat头姿检测偏航角门限0~180
FD_PARAM_FACE_ALIGN_PITCHTHRESfloat头姿检测俯仰角门限0~180
FD_PARAM_FACE_ALIGN_ROLLTHRESfloat头姿检测翻滚角门限0~180
FD_PARAM_ANTI_SPOOFING_THRESfloat活体识别门限0~1

2.2.2 人脸检测 + 特征提取 — sample\_face\_detect

  • 此函数完成了人脸检测以及人脸特征的提取,然后将提取到的人脸数据发送到网页显示,人脸特征数据放在了face\_register\_t.feature中
int sample_face_detect(fd_t *fd, struct video_buffer *vbuf, struct video_format *fmt)
    {
        ...
        fd_exec(fd, &pic_buf, &result);  /* 人脸检测 */
        ...
        webusb_send_pic(vbuf, &result);  /* 网页显示 */
    }
  • 显然如果一副图像中有多个人脸时,其实只保存了得分最高的人脸的feature
if(result.detect_cnt != 0 && result.detect_data[0].feature_dim == FD_MAX_FEATURE_DIMS)
     {
         memcpy((void *)fr.feature, (void *)result.detect_data[0].feature, result.detect_data[0].feature_dim * sizeof(float));
         fr.get_new_feature = true;
     }

2.2.3 摁键回调函数实现人脸注册、比较等人机交互任务 — button_callback

摁键回调函数主要是负责完成人脸注册,特征比较,清楚注册等任务:

  1. 人脸注册:长按开发板上的“用户按钮”,直到日志信息处出现face_recognize: success即表示当前人脸注册成功;
  2. 特征比较:短按开发板上的“用户按钮” ,设备会将当前人脸特征与已注册的人脸库进行逐一比较,如分值超过设定的阈值,则日志信息处会出现face_calc_similar: success,如低于设定的阈值,则显示face_calc_similar: fail
  3. 清除注册:信息快速双击开发板上的“用户按钮”,日志信息处出现clear_face _data: success,则表示人脸库已清除。
void button_callback(uint32_t event)
    {
        switch(event){
            case BUTTON_SINGLE_CLICK:特征比较
            case BUTTON_LONG_PRESS:人脸注册
            case BUTTON_DOUBLE_CLICK:清除注册信息
         }    
    }

通过长按摁键可以实现人脸注册,但是重新上电后,注册的信息就丢失了,所以我们可以把注册的人脸信息存储到非易失的flash中去

3. littlefs文件系统的使用

获取sample

csk6 sdk提供了littlefs的使用示例,可以通过Lisa命令获取示例项目:

通过Lisa命令创建项目:

lisa zep create

按以下目录选择完成sample创建:

sample → subsys → fs → littlefs

开发者也可以通过该连接下载已经打包好的空系统文件:littlefs_image.bin。文件系统bin文件制作完成后,在下文烧录固件时烧录到flash对应的偏移地址上。

https://docs.listenai.com/assets/files/littlefs_imge-9c176f220342bc509ef31d5d9c06bb94.bin

编译

在app根目录下通过以下指令完成编译:

lisa zep build -b csk6011a_nano

烧录

  • 烧录应用项目固件

csk6011a_nano开发板通过USB连接PC,通过烧录指令开始烧录:

lisa zep flash
  • 烧录文件系统bin文件

这里提供串口烧录的指令示例,开发可根据实际的硬件环境选择对应的烧录方式,需要注意偏移地址是正确的。

lisa zep exec cskburn -s \\.\COMx -C 6 0x700000 xxx\littlefs_imge.bin -b 748800

烧录文件系统中。。。

连上串口,复位后得到输出

4. 注册人脸保存到flash

4.1 主要逻辑

实现人脸数据掉电恢复的逻辑很简单,主要包括两个阶段:

  • 写已注册人脸数据到flash
  • 重新上电后读人脸数据到运行时结构体

4.2 最终效果

如图所示,在重新上电后,

  • 首先从flash读取了上次存储的两张人脸特征数据填充到了运行时结构体中
  • 短摁进行人脸识别,人脸识别成功,证明已经成功读取。(本次上电并未手动录入)
  • 长摁进行人脸注册,显示新增人脸成功,将三张结构体都保存到了flash中
    日志信息:
    2022-11-19 21:08:53 read 2 face features from flash!
    2022-11-19 21:09:01 face_calc_similar: success
    2022-11-19 21:09:15 face_recognize: success, count:3
    2022-11-19 21:09:15 save 3 face features to flash!

4.3 详细实现过程

4.3.1 组件配置修改

在prj.conf修改组件配置,添加如下的字段。

# 启用flash
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_FLASH_PAGE_LAYOUT=y
# 启动文件系统
CONFIG_FILE_SYSTEM=y
CONFIG_FILE_SYSTEM_LITTLEFS=y

4.3.2 设备树配置修改

修改设备树配置文件,配置挂载点并指定文件系统在flash的偏移地址。

/ {
    fstab {
        compatible = "zephyr,fstab";
        lfs1: lfs1 {
            compatible = "zephyr,fstab,littlefs";
            mount-point = "/lfs1";
            partition = <&storage_partition>;
            automount ;
            no-format;
            read-size = <16>;
            prog-size = <16>;
            cache-size = <64>;
            lookahead-size = <32>;
            block-cycles = <512>;
        };
    };

};

&flash0 {
  partitions {
    algo_cp_partition: partition@400000 {
      label = "algo_cp";
      reg = <0x400000 0x100000>;
    };
    algo_res_partition: partition@500000 {
      label = "algo_res";
      reg = <0x500000 0x800000>;
    };

    /* storage: 1MB for storage */
    storage_partition: partition@0xD00000 {
    label = "storage";
    reg = <0xD00000 0x100000>;
        };
  };
};

4.3.3 在storage.c文件中实现以下函数:

  • 挂载文件系统
int init_mount_flash()
    {
        int32_t ret = 0;
        snprintf(fname, sizeof(fname), "%s/face_features.bin", mp->mnt_point);
        struct fs_statvfs sbuf;
        ret = littlefs_mount(mp);
        if (ret < 0) {
            return;
        }
    
        ret = fs_statvfs(mp->mnt_point, &sbuf);
        if (ret < 0) {
            LOG_PRINTK("FAIL: statvfs: %d\n", ret);
        }
    
        LOG_PRINTK("%s: bsize = %lu ; frsize = %lu ;"
               " blocks = %lu ; bfree = %lu\n",
               mp->mnt_point,
               sbuf.f_bsize, sbuf.f_frsize,
               sbuf.f_blocks, sbuf.f_bfree);
        return ret;
    }
  • 卸载文件系统
    int unmount_flash() { int ret = 0; ret = fs_unmount(mp); LOG_PRINTK(“%s unmount: %d\n”, mp->mnt_point, ret); return ret; }
  • 写已注册的人脸特征数据到flash
int write_flash_face_data()
    {
        int32_t ret     = 0;
        uint32_t cnt    = 0;
        struct fs_dirent dirent;
        struct fs_file_t file;
    
        fs_file_t_init(&file);
    
        ret = fs_open(&file, fname, FS_O_CREATE | FS_O_RDWR);
        if (ret < 0) {
            LOG_ERR("FAIL: open %s: %d", log_strdup(fname), ret);
            return ret;
        }
    
        ret = fs_stat(fname, &dirent);
        if (ret < 0) {
            LOG_ERR("FAIL: stat %s: %d", log_strdup(fname), ret);
            goto out;
        }
    
        // 获取已注册人脸个数
        ret = storage_get_data_cnt(&cnt);
        CHECK_ERROR(0 == ret, -1);
        LOG_PRINTK("storage_get_data_cnt: %d\n", cnt);
    
        // 先在文件中写已注册人脸个数
        ret = fs_write(&file, &cnt, sizeof(int32_t));
        if (ret < 0) {
            LOG_ERR("FAIL: write %s: %d", log_strdup(fname), ret);
            goto out;
        }
    
        // 接着依次写入人脸特征数据
        float features[384] = {0};
        for(uint32_t i = 0; i < cnt; i++)
        {
            storage_read_data(i, (void *)features, sizeof(features));
            ret = fs_write(&file, features, sizeof(features));
            if (ret < 0) 
            {
                LOG_ERR("FAIL: write %s: %d", log_strdup(fname), ret);
                goto out;
            }
        }
    
    out:
        ret = fs_close(&file);
        if (ret < 0) {
            LOG_ERR("FAIL: close %s: %d", log_strdup(fname), ret);
            return ret;
        }
        return ret;
    }
  • 读存储在flash中的人脸特征数据到运行时结构体
    int read_flash_face_data(int* count) { int32_t ret = 0; uint32_t cnt = 0; struct fs_dirent dirent; struct fs_file_t file; fs_file_t_init(&file); ret = fs_open(&file, fname, FS_O_CREATE | FS_O_RDWR); if (ret < 0) { LOG_ERR(“FAIL: open %s: %d”, log_strdup(fname), ret); return ret; } ret = fs_stat(fname, &dirent); if (ret < 0) { LOG_ERR(“FAIL: stat %s: %d”, log_strdup(fname), ret); goto out; } // 先读出已经注册的人脸个数 ret = fs_read(&file, &cnt, sizeof(int32_t)); if (ret < 0) { LOG_ERR(“FAIL: read %s: %d”, log_strdup(fname), ret); goto out; } LOG_PRINTK(“fs_read: %d”, cnt); // 依次读出flash中的人脸特征信息填充到runtime人脸信息结构体中 float features[384] = {0}; for(uint32_t i = 0; i < cnt; i++) { ret = fs_read(&file, features, sizeof(features)); if (ret < 0) { LOG_ERR(“FAIL: write %s: %d”, log_strdup(fname), ret); goto out; } storage_write((void *)features, sizeof(features)); } *count = cnt; out: ret = fs_close(&file); if (ret < 0) { LOG_ERR(“FAIL: close %s: %d”, log_strdup(fname), ret); return ret; } }
    4.3.4 主函数逻辑
    int main() { init_mount_flash(); read_flash_face_data(); … while(1) { … if(新注册人脸) { write_flash_face_data(); } … } unmount_flash(); }
    5. 总结

1、板子资料挺详细的,基础应用和高级应用都有例程。环境搭建方便,基本根据命令无脑操作,好评。

2、AI功能暂时没有开放模型编译器,不能部署自己的模型,可玩性较低,期待开放模型转bin工具链。

参考资料:

https://docs.listenai.com/chips/600X/ai_usage/fd/intro
https://aijishu.com/a/1060000000365321
https://aijishu.com/a/1060000000361009

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值