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

  • Post author:
  • Post category:其他




前言

  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

    里缩放输出960×540,会发现存储得到的图像数据有异常。修改输出尺寸为960×544后正常;
// 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包是某宝上买的吧。



版权声明:本文为wuu19原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。