waterman 发表于 2024-12-4 03:05

AI挑战营(进阶):6. 多人人脸识别+rtsp

本帖最后由 waterman 于 2024-12-4 09:35 编辑

基于 https://github.com/LuckfoxTECH/luckfox_pico_rkmpi_example.git 以luckfox_pico_rtsp_retinaface为例,进行修改
# 新建工程
新复制一份改名为
```
luckfox_pico_rtsp_retinafacenet
```
并在build.sh中添加


重新编译生成luckfox_pico_rtsp_retinafacenet_demo,上传至板卡测试可以正常运行:

# 工程修改
经过对比,retinaface.h为retinaface_facenet.h的子集,因此直接对其进行替换。
经测试可以直接替换并正常使用。工程目录如下:

仿照luckfox_pico_retinaface_facenet对工程进行了修改,添加了facenet于luckfox_pico_rtsp_retinafacenet_one_shot,实现了单个人脸的录入与识别。并于luckfox_pico_rtsp_retinafacenet_three_face中实现了一个及三个人脸的录入与识别。

具体思路为在while(1)大循环中,每次获取一帧摄像头采集到的图像数据,输入到retinaface中,得到n个检测到的人脸,分别对每个人脸进行识别并标注信息,最后将其进行推流。

关键代码如下:
1. 模型加载
```python
    // Rknn model
    rknn_app_context_t app_retinaface_ctx;
    rknn_app_context_t app_facenet_ctx;
    object_detect_result_list od_results;

    const char *retinaface_model_path = "./model/retinaface.rknn";
    const char *facenet_model_path = "./model/mobilefacenet.rknn";

    memset(&app_retinaface_ctx, 0, sizeof(rknn_app_context_t));
    memset(&app_facenet_ctx, 0, sizeof(rknn_app_context_t));

    //Init Model
    if(init_retinaface_facenet_model(retinaface_model_path, facenet_model_path, &app_retinaface_ctx, &app_facenet_ctx) != RK_SUCCESS)
    {
      RK_LOGE("rknn model init fail!");
      return -1;
    }
```
2. 录入三个参考人脸
```python
    //Init Opencv-mobile
    int ret;
    cv::Mat retinaface_input(retinaface_height, retinaface_width, CV_8UC3, app_retinaface_ctx.input_mems->virt_addr);
    cv::Mat facenet_input(facenet_height, facenet_width, CV_8UC3, app_facenet_ctx.input_mems->virt_addr);

    //Get referencve img feature
    const char *image1_path = "./model/test1.png";
    const char *image2_path = "./model/test2.png";
    const char *image3_path = "./model/test3.png";
    //get image 1 feature
    cv::Mat image = cv::imread(image1_path);
    letterbox(image,facenet_input);//resize image to 160x160

    ret = rknn_run(app_facenet_ctx.rknn_ctx, nullptr);
    if (ret < 0) {
      printf("rknn_run fail! ret=%d\n", ret);
      return -1;
    }

    uint8_t*output = (uint8_t *)(app_facenet_ctx.output_mems->virt_addr);
    float* reference1_out_fp32 = (float*)malloc(sizeof(float) * 128);
    output_normalization(&app_facenet_ctx,output,reference1_out_fp32);

    //get image 2 feature
    image = cv::imread(image2_path);
    letterbox(image,facenet_input);//resize image to 160x160

    ret = rknn_run(app_facenet_ctx.rknn_ctx, nullptr);
    if (ret < 0) {
      printf("rknn_run fail! ret=%d\n", ret);
      return -1;
    }

    output = (uint8_t *)(app_facenet_ctx.output_mems->virt_addr);
    float* reference2_out_fp32 = (float*)malloc(sizeof(float) * 128);
    output_normalization(&app_facenet_ctx,output,reference2_out_fp32);

    //get image 3 feature
    image = cv::imread(image3_path);
    letterbox(image,facenet_input);//resize image to 160x160

    ret = rknn_run(app_facenet_ctx.rknn_ctx, nullptr);
    if (ret < 0) {
      printf("rknn_run fail! ret=%d\n", ret);
      return -1;
    }

    output = (uint8_t *)(app_facenet_ctx.output_mems->virt_addr);
    float* reference3_out_fp32 = (float*)malloc(sizeof(float) * 128);

    output_normalization(&app_facenet_ctx,output,reference3_out_fp32);
```
3. 人脸检测与识别
```
            cv::resize(bgr, retinaface_input, cv::Size(retinaface_width ,retinaface_height), 0, 0, cv::INTER_LINEAR);   
            // memcpy(app_retinaface_ctx.input_mems->virt_addr, model_bgr.data, retinaface_width * retinaface_height * 3);
            inference_retinaface_model(&app_retinaface_ctx, &od_results);
            
            for(int i = 0; i < od_results.count; i++)
            {
                //Get det
                object_detect_result *det_result = &(od_results.results);
                           
                sX = (int)((float)det_result->box.left   *scale_x);
                sY = (int)((float)det_result->box.top    *scale_y);
                eX = (int)((float)det_result->box.right*scale_x);
                eY = (int)((float)det_result->box.bottom *scale_y);
                // printf("%d %d %d %d\n",sX,sY,eX,eY);
                cv::rectangle(frame,cv::Point(sX,sY),cv::Point(eX,eY),cv::Scalar(0,255,0),3);

                //Face capture
                cv::Rect roi(sX,sY,
                            (eX - sX),
                            (eY- sY));
                cv::Mat face_img = frame(roi);

                letterbox(face_img,facenet_input);
                //rknn run
                ret = rknn_run(app_facenet_ctx.rknn_ctx, nullptr);
                if (ret < 0) {
                  printf("rknn_run fail! ret=%d\n", ret);
                  return -1;
                }

                output = (uint8_t *)(app_facenet_ctx.output_mems->virt_addr);

                output_normalization(&app_facenet_ctx, output, out_fp32);
                float norm1 = get_duclidean_distance(reference1_out_fp32,out_fp32);
                float norm2 = get_duclidean_distance(reference2_out_fp32,out_fp32);
                float norm3 = get_duclidean_distance(reference3_out_fp32,out_fp32);
                float min_norm;
                int min_index;

                printf("@ (%d %d %d %d) %.3f %.3f %.3f\n",sX,sY,eX,eY,norm1,norm2,norm3);
                if(norm1 < norm2 && norm1 < norm3)
                {
                  min_norm = norm1;
                  min_index = 1;
                }
                else if(norm2 < norm1 && norm2 < norm3)
                {
                  min_norm = norm2;
                  min_index = 2;
                }
                else
                {
                  min_norm = norm3;
                  min_index = 3;
                }

                //Draw text
                if(min_norm < 1.1)
                {
                  sprintf(show_text, "face%d,norm=%.3f", min_index, min_norm);
                }
                else
                {
                  sprintf(show_text, "unknow");
                }
                cv::putText(frame, show_text, cv::Point(sX, sY - 8),
                                           cv::FONT_HERSHEY_SIMPLEX,0.5,
                                           cv::Scalar(0,255,0),
                                           1);
            }
```
4. 内存及模型释放
```python
    // Release rknn model
    free(reference1_out_fp32);
    free(reference2_out_fp32);
    free(reference3_out_fp32);
    free(out_fp32);

    release_facenet_model(&app_facenet_ctx);
    release_retinaface_model(&app_retinaface_ctx);
```
# 效果测试
网上随便找了一张待检测图片如下:

我们录入的三个人脸依次如下:

## 单张人脸识别

## 三张人脸识别


# 演示视频
## 单张人脸

dda1b4aefd9569fac011d0de3986f3b8<br/>

## 多张人脸

c5cb6a993c0e33bc23e419b5abb762e4<br/>

# 结果分析
目前虽然基本实现功能,但是效果还不是特别好。考虑可能会是以下几点原因的影响:
1. 人脸录入需自行截取人脸部分的图片,直接输入facenet,因此检测结果不是很理想。考虑可以在录入的时候加入retinaface,尝试能否提升检测的准确性。
2. 检测的准确性受阈值的影响,可适当调节检测的阈值来提高识别效果。
3. 还有可能是模型量化时量化数据太少的问题。

此外,也可以额外加上fps显示实时帧率。

秦天qintian0303 发表于 2024-12-4 10:44

<p>录入的是三个,为什么检测圈出来的基本上都是4个&nbsp;&nbsp;</p>
页: [1]
查看完整版本: AI挑战营(进阶):6. 多人人脸识别+rtsp