一.本章节内容:

本章节详细说明show_vi_thread线程的工作流程。该线程的核心功能包括:

  1. 从第二VI模块获取视频数据
  2. 接收rockx_vi_handle_thread推理线程处理的人脸识别结果(包含人脸名称和区域信息,即rockx_object_array_t结构体)
  3. 使用OpenCV将人脸区域和名称标注在视频画面上
  4. 将处理后的视频数据传输至VENC编码器进行压缩编码

二.show_vi_thread线程大体框图

该流程图展示了show_vi_thread线程的工作流程:首先获取第二个VI模块的视频数据,随后提取人脸识别结果(包括识别名称字符串和人脸区域信息rockx_object_array_t),接着使用OpenCV绘制人脸区域矩形框并显示识别名称,最后将处理后的视频数据发送至VENC编码器。

三.show_vi_thread线程代码

3.1. 获取第二个模块VI模块的视频数据

        mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VI, 1, -1);
        if (!mb)
        {
            printf("RK_MPI_SYS_GetMediaBuffer Break.....\n");
            break;
        }

上面是通过RK_MPI_SYS_GetMediaBuffer获取第二个模块的VI数据,模块号是RK_ID_VI,通道号是1。

3.2. 创建OPENCV并获取人脸名称和区域信息

        rockx_object_array_t face_array = get_rockx_face_array();
        People people = get_rockx_people();
        Mat tmp_img = Mat(1080, 1920, CV_8UC1, RK_MPI_MB_GetPtr(mb));
        string face_count_str = std::to_string(face_array.count);
        cv::putText(tmp_img, face_count_str, pointer, cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(255, 0, 255), 3);

上述处理流程通过OpenCV对RKMEDIA获取的VI数据进行操作,具体实现是将数据转换为Mat矩阵格式(Mat rv1126_mat = Mat(HEIGHT, WIDTH, CV_8UC1, RK_MPI_MB_GetPtr(mb)))。随后,从rokx处理线程中获取人脸区域信息(get_rockx_face_array)及对应的人脸名称(get_rockx_people)。

3.3. 利用OPENCV显示人脸区域信息和人脸名称

 for (int i = 0; i < face_array.count; i++)
        {
            int x = face_array.object[i].box.left * x_rate;//转换后的显示画面左边界坐标 计算人脸框在显示画面上的左上角水平坐标
            int y = face_array.object[i].box.top * y_rate;//转换后的显示画面上边界坐标 计算人脸框在显示画面上的左上角垂直坐标
            int w = (face_array.object[i].box.right - face_array.object[i].box.left) * x_rate;//计算人脸框在显示画面上的宽度
            int h = (face_array.object[i].box.bottom - face_array.object[i].box.top) * y_rate;//计算人脸框在显示画面上的高度
            //设置边界保护
            if (x < 0)
                x = 0;
            if (y < 0)
                y = 0;

            while ((uint32_t)(x + w) >= 1920)
            {
                w -= 16;
            }
            while ((uint32_t)(y + h) >= 1080)
            {
                h -= 16;
            }

            Scalar color(255, 0, 255);
            Rect boundingBox(x, y, 500, 500);
            int thickness = 3;
            rectangle(tmp_img, boundingBox, color, thickness);

            int baseline;
            Size text_size = getTextSize(people.people_name, 2, 2, 2, &baseline);
            Point origin;
            origin.x = tmp_img.cols / 4 - text_size.width / 4;
            origin.y = tmp_img.rows / 4 + text_size.height / 4;
            cv::putText(tmp_img, people.people_name, origin, cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(255, 0, 255), 3);
        }

这段代码使用OpenCV来显示人脸区域和对应的名称。首先通过循环遍历每个人脸的区域信息,每个区域信息存储在一个box对象中,包含四个坐标参数(left、top、right、bottom)。基于这些坐标计算出x和y值(具体计算方法见上图)。

然后调用OpenCV的rectangle函数绘制人脸区域矩形,该矩形的宽度和高度都固定设置为500像素。在绘制完人脸矩形后,使用OpenCV的putText函数将get_rockx_people获取的人脸名称显示在VI模块中。

3.4. 把处理后的VI数据发送到VENC编码器并释放资源

RK_MPI_SYS_SendMediaBuffer(RK_ID_VENC, 0, mb);
RK_MPI_MB_ReleaseBuffer(mb);

VI数据经过RK_MPI_SYS_SendMediaBuffer接口传输至VENC编码器后,编码器即可生成H.264格式的编码数据。

四.完整代码

void *show_vi_thread(void *args)
{
    pthread_detach(pthread_self());
    MEDIA_BUFFER mb = NULL;

    float x_rate = (float)1920 / 1920;//计算 X 轴(水平方向)的坐标缩放比例
    float y_rate = (float)1920 / 1080;//计算 Y 轴(垂直方向)的坐标缩放比例    

    Point pointer;
    pointer.x = 300;
    pointer.y = 300;

    while (1)
    {
        mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VI, 1, -1);
        if (!mb)
        {
            printf("RK_MPI_SYS_GetMediaBuffer Break.....\n");
            break;
        }

        rockx_object_array_t face_array = get_rockx_face_array();
        People people = get_rockx_people();
        Mat tmp_img = Mat(1080, 1920, CV_8UC1, RK_MPI_MB_GetPtr(mb));
        string face_count_str = std::to_string(face_array.count);
        cv::putText(tmp_img, face_count_str, pointer, cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(255, 0, 255), 3);

        for (int i = 0; i < face_array.count; i++)
        {
            int x = face_array.object[i].box.left * x_rate;//转换后的显示画面左边界坐标 计算人脸框在显示画面上的左上角水平坐标
            int y = face_array.object[i].box.top * y_rate;//转换后的显示画面上边界坐标 计算人脸框在显示画面上的左上角垂直坐标
            int w = (face_array.object[i].box.right - face_array.object[i].box.left) * x_rate;//计算人脸框在显示画面上的宽度
            int h = (face_array.object[i].box.bottom - face_array.object[i].box.top) * y_rate;//计算人脸框在显示画面上的高度
            //设置边界保护
            if (x < 0)
                x = 0;
            if (y < 0)
                y = 0;

            while ((uint32_t)(x + w) >= 1920)
            {
                w -= 16;
            }
            while ((uint32_t)(y + h) >= 1080)
            {
                h -= 16;
            }

            Scalar color(255, 0, 255);
            Rect boundingBox(x, y, 500, 500);
            int thickness = 3;
            rectangle(tmp_img, boundingBox, color, thickness);

            int baseline;
            Size text_size = getTextSize(people.people_name, 2, 2, 2, &baseline);
            Point origin;
            origin.x = tmp_img.cols / 4 - text_size.width / 4;
            origin.y = tmp_img.rows / 4 + text_size.height / 4;
            cv::putText(tmp_img, people.people_name, origin, cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(255, 0, 255), 3);
        }

        RK_MPI_SYS_SendMediaBuffer(RK_ID_VENC, 0, mb);
        RK_MPI_MB_ReleaseBuffer(mb);
    }

    return NULL;
}

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐