RK3588 RGA 图像操作

Posted 基层搬砖的Panda

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RK3588 RGA 图像操作相关的知识,希望对你有一定的参考价值。

背景

公司业务需要用到RK3588 的RGA进行图像处理加速,网上搜了下,这方面的资料很少,在此记录下自己从熟悉文档到应用的整个过程,给有相关需求的小伙伴做个参考。

一、什么是RGA

RGA (Raster Graphic Acceleration Unit)是一个独立的2D硬件加速器,可用于加速点/线绘制,执行图像缩放、旋转、格式转换等常见的2D图形操作。

二、RK3588 RGA及代码示例

2.1 从git拉取官方文档及sample示例

git clone https://github.com/airockchip/librga
cd librga


其中 include 是相关头文件,libs是运行库,samples是代码示例。注意:官方demo是有默认的验证源文件,开始前先看下图对应的md文件。

2.2 图像缩放或者放大

本示例代码是在官方resize_demo的基础上进行改动、验证。说明:因为是Debian系统,安装opencv会报错,缺少libjasper库。网上搜了下比较麻烦,本人使用的先在Ubuntu先编译好的opencv库。

代码功能:使用opencv读取本地 1.jpg 图片,调用RGA resize接口进行图片缩小和放大,再使用opencv保存为新的文件。BIG宏定义是用来执行控制放大还缩操作。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "im2d_version.h"
#include "im2d_type.h"
#include "im2d_single.h"
#include "im2d_common.h"
#include "im2d_buffer.h"
#include "RgaUtils.h"
#include "src/utils/utils.h"
#include "./opencv2/core/core.hpp"
#include "./opencv2/highgui/highgui.hpp"

using namespace std;
using namespace cv;

#define BIG

#ifdef BIG
#define RESIZE_WIDTH  1920
#define RESIZE_HEIGHT 1080
#define SCALE_NAME "./scale_1920_1080.jpg"
#else
#define RESIZE_WIDTH  640
#define RESIZE_HEIGHT 480
#define SCALE_NAME "./zoom_640_480.jpg"
#endif

int main(int argc, char **argv)

    clock_t t1, t2;
    t1 = clock();
    int ret = 0;
    int src_width, src_height, src_format;
    int dst_width, dst_height, dst_format;
    char *src_buf, *dst_buf;
    int src_buf_size, dst_buf_size;

    rga_buffer_t src_img, dst_img;
    rga_buffer_handle_t src_handle, dst_handle;

    memset(&src_img, 0, sizeof(src_img));
    memset(&dst_img, 0, sizeof(dst_img));

    Mat image, res;
	image = imread("./1.jpg");
	if (image.data == nullptr)                     
	
		cout << "图片文件不存在" << endl;
	

    cout << "图像宽为:" << image.cols << "\\t高度为:" << image.rows << "\\t通道数为:" << image.channels() << endl;
    src_width = image.cols;
    src_height = image.rows;
    src_format = RK_FORMAT_BGR_888;

    // src_format = RK_FORMAT_RGBA_8888; // RK_FORMAT_YCbCr_420_SP

    dst_width = RESIZE_WIDTH;
    dst_height = RESIZE_HEIGHT;
    dst_format = RK_FORMAT_BGR_888;


    src_buf_size = src_width * src_height * get_bpp_from_format(src_format);
    dst_buf_size = dst_width * dst_height * get_bpp_from_format(dst_format);
    cout << " src format: " << get_bpp_from_format(src_format) << endl;
    cout << " dst format: " << get_bpp_from_format(dst_format) << endl;

    src_buf = (char *)malloc(src_buf_size);
    dst_buf = (char *)malloc(dst_buf_size);


    memcpy(src_buf, image.data, src_width * src_height * get_bpp_from_format(src_format));

    memset(dst_buf, 0x80, dst_buf_size);

    src_handle = importbuffer_virtualaddr(src_buf, src_buf_size);
    dst_handle = importbuffer_virtualaddr(dst_buf, dst_buf_size);
    if (src_handle == 0 || dst_handle == 0) 
        printf("importbuffer failed!\\n");
        // goto release_buffer;
        return -1;
    

    src_img = wrapbuffer_handle(src_handle, src_width, src_height, src_format);
    dst_img = wrapbuffer_handle(dst_handle, dst_width, dst_height, dst_format);


    ret = imcheck(src_img, dst_img, , );
    if (IM_STATUS_NOERROR != ret) 
        printf("%d, check error! %s", __LINE__, imStrError((IM_STATUS)ret));
        return -1;
    
    printf("%d, check success \\n", __LINE__);


    ret = imresize(src_img, dst_img);
    if (ret == IM_STATUS_SUCCESS) 
        printf("imresize running success!\\n");
     else 
        printf("running failed, %s\\n", imStrError((IM_STATUS)ret));
        // goto release_buffer;
        return -1;
    
    t2 = clock();

    double time_use = (double)(t2 - t1) / CLOCKS_PER_SEC; // 微秒
    printf("vptdt_init  time_use is [%f] s\\n", time_use);
    
    res.create(RESIZE_HEIGHT, RESIZE_WIDTH, CV_8UC3);
    memcpy(res.data, dst_buf, RESIZE_HEIGHT*RESIZE_WIDTH*3);

    cv::imwrite(SCALE_NAME, res);
    printf("save picture: [ %s ] success\\n", SCALE_NAME);


release_buffer:
    if (src_handle)
        releasebuffer_handle(src_handle);
    if (dst_handle)
        releasebuffer_handle(dst_handle);

    if (src_buf)
        free(src_buf);
    if (dst_buf)
        free(dst_buf);
    return ret;

    return 0;

2.2 图像格式转换

本示例代码是在官方cvtcolor_demo的基础上进行改动、验证。

代码功能:

  1. 打开宏定义BGR2NV12 ,使用opencv读取本地1.jpg图片,调用RGA imcvtcolor 接口实现BGR到YUV的转换;
  2. 关闭宏定义BGR2NV12,读取YUV文件,调用RGA imcvtcolor 接口实现YUV到BGR的转换;
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "im2d_version.h"
#include "im2d_type.h"
#include "im2d_single.h"
#include "im2d_common.h"
#include "im2d_buffer.h"
#include "RgaUtils.h"
#include "src/utils/utils.h"
#include "./opencv2/core/core.hpp"
#include "./opencv2/highgui/highgui.hpp"

#include "./opencv4/opencv2/opencv.hpp"
using namespace std;
using namespace cv;

#define TRANSFER_FILE "./transfer.YUV"
#define RGA_WRITE_FILE "./rga_res.jpg"
#define OPENCV_WRITE_FILE "./opecv_res.jpg"
#define BGR2NV12
// #define OPENCV_TRANSFER
int main(int argc, char **argv)


    int ret = 0;
    int src_width, src_height, src_format;
    int dst_width, dst_height, dst_format;
    char *src_buf, *dst_buf;
    int src_buf_size, dst_buf_size;

    rga_buffer_t src_img, dst_img;
    rga_buffer_handle_t src_handle, dst_handle;

    memset(&src_img, 0, sizeof(src_img));
    memset(&dst_img, 0, sizeof(dst_img));

#ifdef BGR2NV12
    clock_t t1, t2;
    t1 = clock();
    Mat image, res;
    image = imread("./1.jpg");
    if (image.data == nullptr)
    
        cout << "图片文件不存在" << endl;
    

    cout << "图像宽为:" << image.cols << "\\t高度为:" << image.rows << "\\t通道数为:" << image.channels() << endl;
    src_width = image.cols;
    src_height = image.rows;
    src_format = RK_FORMAT_BGR_888;

    dst_width = image.cols;
    dst_height = image.rows;
    dst_format = RK_FORMAT_YCbCr_420_SP; // NV12
    cout << " src format: " << get_bpp_from_format(src_format) << endl;
    cout << " dst format: " << get_bpp_from_format(dst_format) << endl;

    src_buf_size = src_width * src_height * get_bpp_from_format(src_format);
    dst_buf_size = dst_width * dst_height * get_bpp_from_format(dst_format);

    src_buf = (char *)malloc(src_buf_size);
    dst_buf = (char *)malloc(dst_buf_size);

    memcpy(src_buf, image.data, src_width * src_height * get_bpp_from_format(src_format));

    memset(dst_buf, 0x80, dst_buf_size);

    src_handle = importbuffer_virtualaddr(src_buf, src_buf_size);
    dst_handle = importbuffer_virtualaddr(dst_buf, dst_buf_size);
    if (src_handle == 0 || dst_handle == 0)
    
        printf("importbuffer failed!\\n");
        if (src_handle)
            releasebuffer_handle(src_handle);
        if (dst_handle)
            releasebuffer_handle(dst_handle);

        if (src_buf)
            free(src_buf);
        if (dst_buf)
            free(dst_buf);
        return ret;
    

    src_img = wrapbuffer_handle(src_handle, src_width, src_height, src_format);
    dst_img = wrapbuffer_handle(dst_handle, dst_width, dst_height, dst_format);

    ret = imcheck(src_img, dst_img, , );
    if (IM_STATUS_NOERROR != ret)
    
        printf("%d, check error! %s", __LINE__, imStrError((IM_STATUS)ret));
        return -1;
    

    // ret = imcvtcolor(src_img, dst_img, src_format, dst_format, IM_RGB_TO_YUV_BT709_LIMIT);
    ret = imcvtcolor(src_img, dst_img, src_format, dst_format, IM_RGB_TO_YUV_BT709_LIMIT);
    if (ret == IM_STATUS_SUCCESS)
    
        printf("imcvtcolor running success!\\n");
    
    else
    
        printf("imcvtcolo rrunning failed, %s\\n", imStrError((IM_STATUS)ret));
        if (src_handle)
            releasebuffer_handle(src_handle);
        if (dst_handle)
            releasebuffer_handle(dst_handle);

        if (src_buf)
            free(src_buf);
        if (dst_buf)
            free(dst_buf);
        return ret;
    
    t2 = clock();
    double time_use = (double)(t2 - t1) / CLOCKS_PER_SEC; // 微秒
    printf("imcvtcolo to YUV time_use is [%f] s\\n", time_use);
    FILE *file = fopen(TRANSFER_FILE, "wb+");
    if (!file)
    
        fprintf(stderr, "Could not open %s\\n", TRANSFER_FILE);
        return false;
    
    else
    
        fprintf(stderr, "open %s and write ok\\n", TRANSFER_FILE);
    
    fwrite(dst_buf, image.cols * image.rows * get_bpp_from_format(dst_format), 1, file);
    fclose(file);

#else
    src_width = 1920;
    src_height = 1080;
    src_format = RK_FORMAT_YCbCr_420_SP; // NV12
#ifdef OPENCV_TRANSFER

    cout << " src format: " << get_bpp_from_format(src_format) << endl;
    src_buf_size = src_width * src_height * get_bpp_from_format(src_format);

    src_buf = (char *)malloc(src_buf_size);
    FILE *file = fopen(TRANSFER_FILE, "rb");
    if (!file)
    
        fprintf(stderr, "Could not open %s\\n", TRANSFER_FILE);
        return -EINVAL;
    
    fread(src_buf, src_width * src_height * get_bpp_from_format(src_format), 1, file);
    fclose(file);

    cv::Mat yuvNV12, rgb24;
    yuvNV12.create(src_height * 3 / 2, src_width, CV_8UC1);
    memcpy(yuvNV12.data, src_buf, src_width*src_height * 3 / 2);
    // trans to rgb24
    cv::cvtColor(yuvNV12, rgb24, cv::COLOR_YUV2BGR_NV12);

    cv::imwrite(OPENCV_WRITE_FILE, rgb24);
    printf("[OPENCV] save picture: [ %s ] success\\n", OPENCV_WRITE_FILE);
    free(src_buf);
    src_buf = NULL;
#else
    clock_t t1, t2;
    t1 = clock();
    dst_width = 1920;
    dst_height = 1080;
    dst_format = RK_FORMAT_BGR_888;

    cout << "src format: " << get_bpp_from_format(src_format) << endl;
    cout << "dst format: " << get_bpp_from_format(dst_format) << endl;

    src_buf_size = src_width * src_height * get_bpp_from_format(src_format);
    dst_buf_size = dst_width * dst_height * get_bpp_from_format(dst_format);

    src_buf = (char *)malloc(src_buf_size);
    dst_buf = (char *)malloc(dst_buf_size);

    FILE *file = fopen(TRANSFER_FILE, "rb");
    if (!file)
    
        fprintf(stderr, "Could not open %s\\n", TRANSFER_FILE);
        return -EINVAL;
    
    fread(src_buf, src_width * src_height * get_bpp_from_format(src_format), 1, file);
    fclose(file);
    printf("read src file success!\\n");
    memset(dst_buf, 0x80, dst_buf_size);

    src_handle = importbuffer_virtualaddr(src_buf, src_buf_size);
    dst_handle = importbuffer_virtualaddr(dst_buf, dst_buf_size);
    if (src_handle == 0 || dst_handle == 0)
    
        printf("importbuffer failed!\\n");
        if (src_handle)
            releasebuffer_handle(src_handle);
        if (dst_handle)
            releasebuffer_handle(dst_handle);

        if (src_buf)
            free(src_buf);
        if (dst_buf)
            free(dst_buf);
        return ret;
    

    src_img = wrapbuffer_handle(src_handle, src_width, src_height, src_format);
    dst_img = wrapbuffer_handle(dst_handle, dst_width, dst_height, dst_format);

    ret = imcheck(src_img, dst_imgRK3399/RK3328具有强大的视频编解码能力,Soc集成的硬件视频编解码器VPU,能同时处理多路视频的编解码,Soc集成的图形加速引擎RGA, 可以高效地处理图形缩放、旋转、颜色空间转换等操作。结合VPU和RGA,可以高效地实现视频流的解码和后处理,作为第三方应用处理的数据源。

技术图片

 

调用API

      Rockchip开发的mpp库,提供了非常易于使用API,通过各种不同的控制项,可以适应多种不同场景的视频编解码应用。

 

硬件准备
 
      RK3399平台:AIO-3399C主板 + 12路摄像头

技术图片

 

 RK3328平台:RK3328行业主板 + 6路摄像头

 

技术图片

 

运行Demo


      Firefly在RK3399和RK3328 Ubuntu平台上, 针对目前最常见的场景,提供了一个多路RTSP流解码的Demo,Demo支持在RK3399平台上, 同时解12路H264/H265 RTSP,测试流帧率为25fps,无丢帧,低延时,CPU占用率较低(基本只用于RTSP流接收和封包)。在RK3328平台上,同时能解6路H264/H265 RTSP。

技术图片

 

运行过程

      Demo中,MPP解码->图像后处理->显示整个流程中,VPU和RGA都是直接访问内存物理地址,整个过程零内存拷贝,效率很高。处理后的数据,通过libdrm接口,直接送往显示接口输出。

 

      Demo对解码后的图像输出数据,只做了显示处理,如果在实际应用中需要对图像做处理,可能会导致帧率下降。

 

演示效果
 
    运行时效果图如下:

 

技术图片

 

实时帧率

实时帧率计算结果如下:

技术图片

安装方法

Demo运行在RK3399 或 RK3328 Ubuntu 18.04平台上。

 
1. 确认系统中存在 /dev/rga 设备节点。 如果没有此节点, 请参考wiki更新最新的固件。
2. 安装:
 
$ sudo apt update    
$ sudo apt install firefly-multi-rtsp

  

3.根据实际环境,修改配置文件。 参照下一节配置参数。
4. 运行:
 $ sudo test_ff_rtsp_dec

  

5. 退出:
 
运行过程中,按Enter键,退出Demo, 由于退出时需要关闭多路rtsp流,所以退出时需要稍作等待。
 
配置参数

Demo中提供了一个配置文件config.json,用于配置demo的运行环境。

配置文件位于/etc/ff_rtsp_dec/config.json ,json格式。(注意不要在config.json中添加注释,否则可能会导致json解析出错)

 
主要配置项如下
 
基础参数:
    "basepara" : {
        "dri_path" : "/dev/dri/card0",  //不要修改
        
        "prefer_monitor_w" : 1920, 
        "prefer_monitor_h" : 1080,
        
        "code_type" : "H265", //rtsp流编码类型,H264/H265
         "line_count" : 12,  //rtsp流数量
         
         "output_w" : 480,  //每一路解码后的输出size.
         "output_h" : 360,
         
         "rotate" : 0 //旋转   未使用
         
         },

  

其中prefer_monitor_w 和 prefer_monitor_h是配置外接显示设备的分辨率。
 
1. 配置外接显示设备的分辨率,仅在显示设备支持多种分辨率(如HDMI、DP等显示器)时有效,如果显示设备支持该分辨率,则将显示设备设置为该分辨率。
 
2. LVDS, MIPI屏该配置无效(但仍需保留该配置项,只是配置值无效),因为LCD支持一种分辨率。
 
3. 配置此参数后,Demo将显示设备设置为对应的分辨率,然后计算该分辨率是否能够完整显示所有路RTSP解码后的输出。如果不能显示,应该减小rtsp流数量,或者减小输出尺寸。
    line_h = prefer_monitor_w / output_w; 
    line_v = prefer_monitor_h / output_h; 
    line_h * line_v <= line_count

  

RTSP 参数

    "rtsp" : [
        {
            "id" : 0, //id
            "url" : "rtsp://168.168.3.100:554/av_stream",   //url
            "usr" : "admin", //username
            "pwd" : "firefly123", //password
            "tcp" : false // Is tcp?
         },
      ...
      ]

  

RTSP参数为一个数组,配置每一路rtsp流的参数。

 

 

———————————————
更多信息请关注Firefly公众微信号fireflytee:
技术图片 
———————————————
Firefly官网:http://www.t-firefly.com

Firefly开源社区:http://developer.t-firefly.com

以上是关于RK3588 RGA 图像操作的主要内容,如果未能解决你的问题,请参考以下文章

ROC-RK3588S-PC八核8K人工智能开源主板

ROC-RK3588S-PC八核8K人工智能开源主板

ROC-RK3588-PC 八核8K人工智能开源主板

ROC-RK3588-PC 八核8K人工智能开源主板

ROC-RK3588-PC 八核8K人工智能开源主板

RK3588实战:调用npu加速,yolov5识别图像ffmpeg发送到rtmp服务器