rv1126 获取图像数据,实现图像裁剪、缩放、旋转【RK_MPI API接口】

前言

  1. 刚接触RK平台,目前正在学习探索阶段,欢迎朋友们一起讨论,指出文章错误和可以优化的地方;
  2. 如果想参照文中描述进行编译、执行程序,请先参考阅读rv1126 SDK编译rv1126 数据流

版本说明,测试使用SDK版本是2020-0912版本,文中记录的问题,可能在新版本已经解决;文中使用的接口函数,可能老版本没有实现。
备注:后续重新购买了2020-1212版本SDK,旋转问题已经解决,不需要再修改源码。另外,编译时,需要多带上如下几个库:-lrknn_runtime -lod_share -lrockx -lOpenVX -lVSC -lGAL -lArchModelSw -lNNArchPerf


代码

直接上代码,代码将同一帧图像数据,利用RK平台的RGA功能,分别对图像进行了格式转换、缩放+旋转+格式转换、裁剪+格式转换,得到3路图像输出,并存储成文件。
代码是参考SDK包rv1126_1109/external/rkmedia/test/c_api/rkmedia_vi_rga_test.c修改而成。
说明:

  1. 编译后,执行可能遇到旋转效果无法实现,可以参考后续说明的方法修改;
  2. RK平台提供的接口,缩放图的长宽应该是要16的倍数,如果参照rv1126_1109/external/rkmedia/test/c_api/rkmedia_vi_rga_test.c里缩放输出960x540,会发现存储得到的图像数据有异常。修改输出尺寸为960x544后正常;
// Copyright 2020 Fuzhou Rockchip Electronics Co., Ltd. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <assert.h>
#include <fcntl.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

#include <pthread.h>

//#include "common/sample_common.h"
#include "rkmedia_api.h"
#include "rkmedia_venc.h"

static bool quit = false;

// 信号处理程序
static void sigterm_handler(int sig)
{
    fprintf(stderr, "signal %d\n", sig);
    quit = true;
}

// 图像数据处理线程
static void *GetMediaBuffer(void *arg)
{
    RGA_CHN rga_chn = *(RGA_CHN *)arg;
    char save_path[512];

    //
    printf("#Start %s thread, rga_chn:%d\n", __func__, rga_chn);
    sprintf(save_path, "/userdata/output_%d.nv12", rga_chn);
    //
    FILE *save_file = fopen(save_path, "w");
    if (!save_file)
        printf("ERROR: Open %s failed!\n", save_path);

    MEDIA_BUFFER mb = NULL;
    int save_cnt = 0;
    int recv_len;
    while (!quit)
    {
        mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_RGA, rga_chn, 50);
        if (!mb)
        {
            if (!quit)
            {
                continue;
            }
            printf("chn-%d:RK_MPI_SYS_GetMediaBuffer get null buffer!\n", rga_chn);
            break;
        }

        recv_len = RK_MPI_MB_GetSize(mb);
        printf("Get Frame-chn-%d:ptr:%p, fd:%d, size:%zu, mode:%d, channel:%d, "
               "timestamp:%lld\n",
               rga_chn,
               RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetFD(mb), recv_len,
               RK_MPI_MB_GetModeID(mb), RK_MPI_MB_GetChannelID(mb),
               RK_MPI_MB_GetTimestamp(mb));

        if (save_file && (save_cnt < 1))
        {
            int rtn = fwrite(RK_MPI_MB_GetPtr(mb), 1, recv_len, save_file);
            fsync(fileno(save_file));
            printf("#Save frame-chn-%d:%d to %s, rtn = %d\n", rga_chn, save_cnt, save_path, rtn);
            save_cnt++;
        }

        RK_MPI_MB_ReleaseBuffer(mb);
    }

    if (save_file)
        fclose(save_file);
    printf("%s-chn-%d - exit\r\n", __func__, rga_chn);

    return NULL;
}

//
int main()
{
    int ret = -1;
    //
    VI_PIPE vi_pipe_0 = 0;
    VI_CHN vi_chn_1 = 1;
    //
    RGA_CHN rga_chn_0 = 0;
    RGA_CHN rga_chn_1 = 1;
    RGA_CHN rga_chn_2 = 2;

    // 初始化mpi sys
    RK_MPI_SYS_Init();

    //
    // 数据源,ISP20的输出:
    // rkispp_m_bypass, 不支持设置分辨率,不支持缩放,         NV12/NV16/YUYV/FBC0/FBC2
    // rkispp_scale0,   max width: 3264,最大支持 8 倍缩放, NV12/NV16/YUYV
    // rkispp_scale1,   max width: 1280,最大支持 8 倍缩放, NV12/NV16/YUYV
    // rkispp_scale2,   max width: 1280,最大支持 8 倍缩放, NV12/NV16/YUYV
    //
    VI_CHN_ATTR_S vi_chn_attr;
    vi_chn_attr.pcVideoNode = "rkispp_scale0";
    vi_chn_attr.u32BufCnt = 4;
    vi_chn_attr.u32Width = 1920;
    vi_chn_attr.u32Height = 1080;
    vi_chn_attr.enPixFmt = IMAGE_TYPE_NV12;
    vi_chn_attr.enWorkMode = VI_WORK_MODE_NORMAL;
    //
    ret = RK_MPI_VI_SetChnAttr(vi_pipe_0, vi_chn_1, &vi_chn_attr);
    ret |= RK_MPI_VI_EnableChn(vi_pipe_0, vi_chn_1);
    if (ret)
    {
        printf("Create vi[%d] failed! ret=%d\n", vi_chn_1, ret);
        return -1;
    }

    //
    // 源数据配置
    //
    MPP_CHN_S stSrcChn;
    stSrcChn.enModId = RK_ID_VI;
    stSrcChn.s32DevId = vi_pipe_0;
    stSrcChn.s32ChnId = vi_chn_1;

    //
    // 输出-0
    // 格式转换 IMAGE_TYPE_NV12 -> IMAGE_TYPE_YUV420P
    //
    RGA_ATTR_S stRgaAttr_0;
    stRgaAttr_0.bEnBufPool = RK_TRUE;
    stRgaAttr_0.u16BufPoolCnt = 4;
    stRgaAttr_0.u16Rotaion = 0;
    stRgaAttr_0.stImgIn.u32X = 0;
    stRgaAttr_0.stImgIn.u32Y = 0;
    stRgaAttr_0.stImgIn.imgType = IMAGE_TYPE_NV12;
    stRgaAttr_0.stImgIn.u32Width = 1920;
    stRgaAttr_0.stImgIn.u32Height = 1080;
    stRgaAttr_0.stImgIn.u32HorStride = 1920;
    stRgaAttr_0.stImgIn.u32VirStride = 1080;
    stRgaAttr_0.stImgOut.u32X = 0;
    stRgaAttr_0.stImgOut.u32Y = 0;
    stRgaAttr_0.stImgOut.imgType = IMAGE_TYPE_YUV420P;
    stRgaAttr_0.stImgOut.u32Width = 1920;
    stRgaAttr_0.stImgOut.u32Height = 1080;
    stRgaAttr_0.stImgOut.u32HorStride = 1920;
    stRgaAttr_0.stImgOut.u32VirStride = 1080;
    //
    ret = RK_MPI_RGA_CreateChn(rga_chn_0, &stRgaAttr_0);
    if (ret)
    {
        printf("Create rga[%d] falied! ret=%d\n", rga_chn_0, ret);
        goto EXIT_0;
    }

    pthread_t read_thread_0;
    pthread_create(&read_thread_0, NULL, GetMediaBuffer, &rga_chn_0);

    //
    MPP_CHN_S stDestChn_0;
    stDestChn_0.enModId = RK_ID_RGA;
    stDestChn_0.s32DevId = 0;
    stDestChn_0.s32ChnId = rga_chn_0;
    //
    ret = RK_MPI_SYS_Bind(&stSrcChn, &stDestChn_0);
    if (ret)
    {
        printf("Bind vi[1] and rga[%d] failed! ret=%d\n", rga_chn_0, ret);
        goto EXIT_1;
    }

    //
    // 输出-1
    // 旋转 270 度
    // 缩放 1920 -> 960
    // 缩放 1080 -> 544
    // 格式转换 IMAGE_TYPE_NV12 -> IMAGE_TYPE_YUV420P
    //
    RGA_ATTR_S stRgaAttr_1;
    stRgaAttr_1.bEnBufPool = RK_TRUE;
    stRgaAttr_1.u16BufPoolCnt = 4;
    stRgaAttr_1.u16Rotaion = 270;
    stRgaAttr_1.stImgIn.u32X = 0;
    stRgaAttr_1.stImgIn.u32Y = 0;
    stRgaAttr_1.stImgIn.imgType = IMAGE_TYPE_NV12;
    stRgaAttr_1.stImgIn.u32Width = 1920;
    stRgaAttr_1.stImgIn.u32Height = 1080;
    stRgaAttr_1.stImgIn.u32HorStride = 1920;
    stRgaAttr_1.stImgIn.u32VirStride = 1080;
    stRgaAttr_1.stImgOut.u32X = 0;
    stRgaAttr_1.stImgOut.u32Y = 0;
    stRgaAttr_1.stImgOut.imgType = IMAGE_TYPE_YUV420P;
    stRgaAttr_1.stImgOut.u32Width = 544;
    stRgaAttr_1.stImgOut.u32Height = 960;
    stRgaAttr_1.stImgOut.u32HorStride = 544;
    stRgaAttr_1.stImgOut.u32VirStride = 960;
    //
    ret = RK_MPI_RGA_CreateChn(rga_chn_1, &stRgaAttr_1);
    if (ret)
    {
        printf("Create rga[%d] falied! ret=%d\n", rga_chn_1, ret);
        goto EXIT_2;
    }

    pthread_t read_thread_1;
    pthread_create(&read_thread_1, NULL, GetMediaBuffer, &rga_chn_1);

    //
    MPP_CHN_S stDestChn_1;
    stDestChn_1.enModId = RK_ID_RGA;
    stDestChn_1.s32DevId = 0;
    stDestChn_1.s32ChnId = rga_chn_1;
    //
    ret = RK_MPI_SYS_Bind(&stSrcChn, &stDestChn_1);
    if (ret)
    {
        printf("Bind vi[1] and rga[%d] failed! ret=%d\n", rga_chn_1, ret);
        goto EXIT_3;
    }

    //
    // 输出-2
    // 裁剪 1920 -> 1000, 取中间部分
    // 裁剪 1080 -> 800, 取中间部分
    // 格式转换 IMAGE_TYPE_NV12 -> IMAGE_TYPE_YUV420P
    //
    RGA_ATTR_S stRgaAttr_2;
    stRgaAttr_2.bEnBufPool = RK_TRUE;
    stRgaAttr_2.u16BufPoolCnt = 4;
    stRgaAttr_2.u16Rotaion = 0;
    stRgaAttr_2.stImgIn.u32X = 460;
    stRgaAttr_2.stImgIn.u32Y = 140;
    stRgaAttr_2.stImgIn.imgType = IMAGE_TYPE_NV12;
    stRgaAttr_2.stImgIn.u32Width = 1000;
    stRgaAttr_2.stImgIn.u32Height = 800;
    stRgaAttr_2.stImgIn.u32HorStride = 1920;
    stRgaAttr_2.stImgIn.u32VirStride = 1080;
    stRgaAttr_2.stImgOut.u32X = 0;
    stRgaAttr_2.stImgOut.u32Y = 0;
    stRgaAttr_2.stImgOut.imgType = IMAGE_TYPE_YUV420P;
    stRgaAttr_2.stImgOut.u32Width = 1000;
    stRgaAttr_2.stImgOut.u32Height = 800;
    stRgaAttr_2.stImgOut.u32HorStride = 1000;
    stRgaAttr_2.stImgOut.u32VirStride = 800;
    //
    ret = RK_MPI_RGA_CreateChn(rga_chn_2, &stRgaAttr_2);
    if (ret)
    {
        printf("Create rga[%d] falied! ret=%d\n", rga_chn_2, ret);
        goto EXIT_4;
    }

    pthread_t read_thread_2;
    pthread_create(&read_thread_2, NULL, GetMediaBuffer, &rga_chn_2);

    //
    MPP_CHN_S stDestChn_2;
    stDestChn_2.enModId = RK_ID_RGA;
    stDestChn_2.s32DevId = 0;
    stDestChn_2.s32ChnId = rga_chn_2;
    //
    ret = RK_MPI_SYS_Bind(&stSrcChn, &stDestChn_2);
    if (ret)
    {
        printf("Bind vi[1] and rga[%d] failed! ret=%d\n", rga_chn_2, ret);
        goto EXIT_5;
    }

    //
    // 监听退出信号
    //
    printf("%s initial finish\n", __func__);
    signal(SIGINT, sigterm_handler);

    //
    // 等待退出信号
    while (!quit)
    {
        usleep(100);
    }

    //
    // 退出
    //
    ret = 0;

EXIT_6:
    RK_MPI_SYS_UnBind(&stSrcChn, &stDestChn_2);
EXIT_5:
    RK_MPI_RGA_DestroyChn(rga_chn_2);
EXIT_4:
    RK_MPI_SYS_UnBind(&stSrcChn, &stDestChn_1);
EXIT_3:
    RK_MPI_RGA_DestroyChn(rga_chn_1);
EXIT_2:
    RK_MPI_SYS_UnBind(&stSrcChn, &stDestChn_0);
EXIT_1:
    RK_MPI_RGA_DestroyChn(rga_chn_0);
EXIT_0:
    RK_MPI_VI_DisableChn(vi_pipe_0, vi_chn_1);

    return ret;
}

如果已经完成SDK包的编译,可以参考如下编译该源码:

arm-linux-gnueabihf-gcc -I/home/user/work/sdk/rv1126_1109/external/rkmedia/include/rkmedia -L/home/user/work/sdk/rv1126_1109/buildroot/output/rockchip_rv1126_rv1109/staging/usr/lib -o rkmedia_vi_get_frame_test rkmedia_vi_get_frame_test.c -lpthread -ldl -lm -lpcre -lz -lglib-2.0 -leasymedia -ldrm -lrockchip_mpp -lavformat -lavcodec -lswresample -lavutil -lliveMedia -lgroupsock -lBasicUsageEnvironment -lUsageEnvironment -lasound -lv4l2 -lv4lconvert -lrga -lRKAP_ANR -lRKAP_Common -lRKAP_AEC -lrknn_api -lrockface -lsqlite3 -lmd_share -lrkaiq -lssl -lcrypto

执行的部分输出如下。可以看到图像数据的时间戳是同一个,个人感觉应该可以说明图像输入的源为同一帧图像。

Get Frame-chn-0:ptr:0xa1d2a000, fd:13, size:3133440, mode:16, channel:0, timestamp:3698073427
Get Frame-chn-1:ptr:0xa1100000, fd:18, size:783360, mode:16, channel:1, timestamp:3698073427
Get Frame-chn-2:ptr:0xa026c000, fd:23, size:1209600, mode:16, channel:2, timestamp:3698073427

Get Frame-chn-0:ptr:0xa2612000, fd:10, size:3133440, mode:16, channel:0, timestamp:3698106734
Get Frame-chn-1:ptr:0xa1340000, fd:15, size:783360, mode:16, channel:1, timestamp:3698106734
Get Frame-chn-2:ptr:0xa05db000, fd:20, size:1209600, mode:16, channel:2, timestamp:3698106734

Get Frame-chn-0:ptr:0xa231a000, fd:11, size:3133440, mode:16, channel:0, timestamp:3698140083
Get Frame-chn-1:ptr:0xa1280000, fd:16, size:783360, mode:16, channel:1, timestamp:3698140083
Get Frame-chn-2:ptr:0xa04b6000, fd:21, size:1209600, mode:16, channel:2, timestamp:3698140083

旋转功能处理

在刚开始测试时,发现无论如何配置旋转参数,输出的图像都无法实现旋转,于是查看了一下rkmedia的源码。在rv1126_1109/external/rkmedia/src/c_api/rkmedia_api.cc文件中发现了一些蹊跷,RK_S32 RK_MPI_RGA_CreateChn(RGA_CHN RgaChn, RGA_ATTR_S *pstRgaAttr)函数里对输入的旋转参数进行校验后,就把他遗忘了,后续再也没有给他出场的机会:

  RK_U16 u16Rotaion = pstRgaAttr->u16Rotaion;
  if ((u16Rotaion != 0) && (u16Rotaion != 90) && (u16Rotaion != 180) &&
      (u16Rotaion != 270)) {
    LOG("ERROR: %s rotation only support: 0/90/180/270!\n", __func__);
    return -RK_ERR_RGA_ILLEGAL_PARAM;
  }

所以,对以下源码进行了修改,大概在2373行:

  // org code: 
  // PARAM_STRING_APPEND_TO(filter_param, KEY_BUFFER_ROTATE, 0);
  // modify code: 
  PARAM_STRING_APPEND_TO(filter_param, KEY_BUFFER_ROTATE, u16Rotaion);

修改后,参考rv1126 SDK编译,删除buildroot/output/rockchip_rv1126_rv1109/build/rkmedia目录,再重新编译,将新编译得到的buildroot/output/rockchip_rv1126_rv1109/staging/usr/lib/libeasymedia.so.1.0.1文件,替换单板上/usr/lib/libeasymedia.so.1.0.1。替换后,重新执行测试程序,旋转功能正常。
出现这个问题,可能是因为我的SDK包是某宝上买的吧。

<think>好的,我需要帮助用户在Rockchip 3588平台上启动和运行rk_mpi_vi_test程序。首先,我得确认用户提到的引用中的路径是否正确,但看起来路径是buildroot/output/rockchip_rk3568/target/usr/bin/rk_mpi_vi_test,可能用户的环境是RK3568,但问题中提到的是3588平台,这可能需要注意环境差异。 接下来,我应该分步骤说明如何启动该程序。首先需要确认程序是否存在于目标设备的/usr/bin目录下。用户可能需要通过ADB或串口登录设备,然后检查文件是否存在。如果不存在,可能需要重新编译或部署。 然后,可能需要设置执行权限,使用chmod +x命令。运行时可能需要指定参数,比如摄像头设备节点,如/dev/video0,或者配置文件路径。用户可能需要根据硬件配置调整这些参数。 另外,可能需要依赖库的问题,如果运行时报错找不到库,需要检查LD_LIBRARY_PATH是否包含库的路径,比如/usr/lib或/usr/local/lib。还有可能需要MIPI摄像头的正确连接和驱动加载,使用lsmod或dmesg来确认驱动是否正常。 最后,建议用户查阅Rockchip的官方文档或联系技术支持,因为不同的SDK版本可能有不同的配置方式。同时提醒用户注意权限问题,可能需要root权限运行程序。 在生成相关问题时,需要考虑用户可能遇到的后续问题,比如如何配置参数、如何排查启动失败的原因,以及如何交叉编译该程序等。这些问题能帮助用户进一步解决可能遇到的困难。</think>### 如何在3588平台上启动和运行rk_mpi_vi_test程序 #### 1. 确认程序路径与权限 rk_mpi_vi_test是Rockchip媒体处理接口的测试工具,用于摄像头数据采集和视频输入测试[^1]。 - 登录3588开发板(通过串口/SSH/ADB) - 检查程序是否存在: ```bash ls /usr/bin/rk_mpi_vi_test ``` - 若不存在,需从编译生成的路径(如`buildroot/output/.../usr/bin/`)复制到设备 - 添加执行权限: ```bash chmod +x /usr/bin/rk_mpi_vi_test ``` #### 2. 运行基础命令 ```bash rk_mpi_vi_test -a 0 -d /dev/video0 -w 1920 -h 1080 -f NV12 ``` 参数说明: - `-a 0`:指定物理通道编号 - `-d /dev/video0`:摄像头设备节点(可通过`v4l2-ctl --list-devices`确认) - `-w/-h`:分辨率设置 - `-f`:图像格式(需与摄像头支持格式匹配) #### 3. 常见问题排查 - **摄像头驱动检测**: ```bash dmesg | grep mipi # 查看MIPI摄像头初始化日志 v4l2-ctl --list-formats -d /dev/video0 ``` - **权限问题**: 若出现`Permission denied`,尝试以root运行: ```bash sudo rk_mpi_vi_test [参数] ``` - **依赖库缺失**: 设置库路径(假设库在/usr/lib): ```bash export LD_LIBRARY_PATH=/usr/lib:$LD_LIBRARY_PATH ``` #### 4. 配置文件使用(可选) 部分SDK版本需要配置文件: ```bash rk_mpi_vi_test -c /etc/rockchip/vi_config.ini ``` 配置文件示例: ```ini [CH0] width = 1920 height = 1080 format = NV12 ```
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值