Rockchip letterbox RGA实现

cpp 复制代码
#include "opencv2/opencv.hpp"
#include "im2d_version.h"
#include "im2d_type.h"
#include "im2d_single.h"
#include "im2d_common.h"
#include "im2d_buffer.h"
#include "RgaUtils.h"
#include "dma_alloc.h"
#include <iostream>
#include <chrono>
#include <filesystem>

using namespace std;

// 对齐宏
#define ALIGN_CEIL(x, align)    ( ((x) + ((align) - 1) ) & ( ~((align) - 1) ) )
#define ALIGN_FLOOR(x, align)   (  (x) & (~((align) - 1) ) )

struct LetterboxParam
{
    int x_pad;
    int y_pad;
    float scale;
    
    LetterboxParam()
    {
        x_pad = 0;
        y_pad = 0;
        scale = 1;
    }
};

cv::Mat rga_letterbox(const cv::Mat& input_image, 
                      LetterboxParam &letterbox_param, 
                      int dst_width, 
                      int dst_height, 
                      int pad_color = 7500402)
{
    int ret = 0;

    if (input_image.empty())
    {
        printf("Error: rga_letterbox, input_image is empty!\n");
        return cv::Mat();
    }
    if (dst_width <= 0 || dst_height <= 0)
    {
        printf("Error: rga_letterbox, invalid target size!\n");
        return cv::Mat();
    }

    rga_buffer_t src_image;
    rga_buffer_t padding_image;
    rga_buffer_t resize_image;
    rga_buffer_handle_t src_rga_buffer_handle;
    rga_buffer_handle_t padding_rga_buffer_handle;
    rga_buffer_handle_t resize_rga_buffer_handle;

    memset(&src_image, 0, sizeof(src_image));
    memset(&padding_image, 0, sizeof(padding_image));
    memset(&resize_image, 0, sizeof(resize_image));

    int src_width = input_image.cols;
    int src_height = input_image.rows;
    int src_format = RK_FORMAT_RGB_888;

    if ((src_width == dst_width) and (src_height == dst_height))
    {
        return input_image;
    }

    // --- src buffer ---
    int src_virtual_width = ALIGN_CEIL(src_width, 16);
    int src_buf_size = src_virtual_width * src_height * get_bpp_from_format(src_format);
    char *src_buf = nullptr;
    int src_dma_fd = 0;

    ret = dma_buf_alloc(DMA_HEAP_DMA32_UNCACHE_PATCH, src_buf_size, &src_dma_fd, (void **)&src_buf);
    if (ret < 0) 
    {
        printf("Error: rga_letterbox, alloc src dma_heap buffer failed!\n");
        return cv::Mat();
    }
    memset(src_buf, 0x00, src_buf_size);
    for (int i = 0; i < src_height; ++i) 
    {
        memcpy(src_buf + i * src_virtual_width * 3, input_image.data + i * src_width * 3, src_width * 3);
    }

    src_rga_buffer_handle = importbuffer_fd(src_dma_fd, src_buf_size);
    if (!src_rga_buffer_handle)
    {
        printf("Error: rga_letterbox, importbuffer failed!\n");
        if (src_buf) dma_buf_free(src_buf_size, &src_dma_fd, src_buf);
        return cv::Mat();
    }

    src_image = wrapbuffer_handle(src_rga_buffer_handle, src_virtual_width, src_height, src_format);
    // src_image.wstride = src_virtual_width;
    // src_image.hstride = src_height;
    // src_image.width   = src_width;
    // src_image.height  = src_height;

    // --- resize buffer ---
    int resize_virtual_width = ALIGN_CEIL(dst_width, 16);
    int resize_buf_size = resize_virtual_width * dst_height * get_bpp_from_format(src_format);
    char *resize_buf = nullptr;
    int resize_dma_fd = 0;

    ret = dma_buf_alloc(DMA_HEAP_DMA32_UNCACHE_PATCH, resize_buf_size, &resize_dma_fd, (void **)&resize_buf);
    if (ret < 0)
    {
        printf("Error: rga_letterbox, alloc resize dma_heap buffer failed!\n");
        if (src_rga_buffer_handle) releasebuffer_handle(src_rga_buffer_handle);
        if (src_buf) dma_buf_free(src_buf_size, &src_dma_fd, src_buf);
        return cv::Mat();
    }
    memset(resize_buf, 0x00, resize_buf_size);

    resize_rga_buffer_handle = importbuffer_fd(resize_dma_fd, resize_buf_size);
    if (!resize_rga_buffer_handle)
    {
        printf("Error: rga_letterbox, importbuffer failed!\n");
        if (resize_buf) dma_buf_free(resize_buf_size, &resize_dma_fd, resize_buf);
        if (src_rga_buffer_handle) releasebuffer_handle(src_rga_buffer_handle);
        if (src_buf) dma_buf_free(src_buf_size, &src_dma_fd, src_buf);
        return cv::Mat();
    }
    resize_image = wrapbuffer_handle(resize_rga_buffer_handle, resize_virtual_width, dst_height, src_format);
    resize_image.wstride = resize_virtual_width;
    resize_image.hstride = dst_height;
    resize_image.width   = dst_width;
    resize_image.height  = dst_height;

    // --- compute padding ---
    int padding_x = 0;
    int padding_y = 0;
    float scale = 1.0;

    float scale_width  = float(src_width)  / dst_width;
    float scale_height = float(src_height) / dst_height;

    char *padding_buf = nullptr;
    int padding_dma_fd = 0;
    int padding_buf_size = 0;

    if (scale_width >= scale_height)
    {
        scale = scale_width;
        float tmp_h = scale * dst_height;
        padding_y = int(abs((tmp_h - src_height) / 2.0));

        int padding_width  = src_virtual_width;
        int padding_height = src_height + padding_y * 2;
        int padding_virtual_width = ALIGN_CEIL(padding_width, 16);
        padding_buf_size = padding_virtual_width * padding_height * get_bpp_from_format(src_format);

        ret = dma_buf_alloc(DMA_HEAP_DMA32_UNCACHE_PATCH, padding_buf_size, &padding_dma_fd, (void **)&padding_buf);
        if (ret < 0) 
        { 
            if (resize_rga_buffer_handle) releasebuffer_handle(resize_rga_buffer_handle);
            if (resize_buf) dma_buf_free(resize_buf_size, &resize_dma_fd, resize_buf);
            if (src_rga_buffer_handle) releasebuffer_handle(src_rga_buffer_handle);
            if (src_buf) dma_buf_free(src_buf_size, &src_dma_fd, src_buf);
            return cv::Mat(); 
        }
        memset(padding_buf, 0x00, padding_buf_size);

        padding_rga_buffer_handle = importbuffer_fd(padding_dma_fd, padding_buf_size);
        if (!padding_rga_buffer_handle) 
        { 
            if (padding_buf) dma_buf_free(padding_buf_size, &padding_dma_fd, padding_buf);
            if (resize_rga_buffer_handle) releasebuffer_handle(resize_rga_buffer_handle);
            if (resize_buf) dma_buf_free(resize_buf_size, &resize_dma_fd, resize_buf);
            if (src_rga_buffer_handle) releasebuffer_handle(src_rga_buffer_handle);
            if (src_buf) dma_buf_free(src_buf_size, &src_dma_fd, src_buf);
            return cv::Mat(); 
        }

        padding_image = wrapbuffer_handle(padding_rga_buffer_handle, padding_virtual_width, padding_height, src_format);
        // padding_image.wstride = padding_virtual_width;
        // padding_image.hstride = padding_height;
        // padding_image.width   = src_virtual_width;
        // padding_image.height  = src_height + 2 * padding_y;

        int padding_left = 0;
        int padding_right = 0;
        int paddings = padding_image.width - src_image.width;
        if (paddings % 2 == 0)
        {
            padding_left = paddings / 2;
            padding_right = padding_left;
        }
        else
        {
            padding_left = paddings / 2;
            padding_right = padding_left + 1;
        }

        ret = immakeBorder(src_image, padding_image, padding_y, padding_y, padding_left, padding_right, IM_BORDER_CONSTANT, pad_color);
    }
    else
    {
        scale = scale_height;
        float tmp_w = scale * dst_width;
        padding_x = int(abs((tmp_w - src_width) / 2.0));

        int padding_width  = src_virtual_width + padding_x * 2;
        int padding_height = src_height;
        int padding_virtual_width = ALIGN_CEIL(padding_width, 16);
        padding_buf_size = padding_virtual_width * padding_height * get_bpp_from_format(src_format);

        ret = dma_buf_alloc(DMA_HEAP_DMA32_UNCACHE_PATCH, padding_buf_size, &padding_dma_fd, (void **)&padding_buf);
        if (ret < 0) 
        {
            if (resize_rga_buffer_handle) releasebuffer_handle(resize_rga_buffer_handle);
            if (resize_buf) dma_buf_free(resize_buf_size, &resize_dma_fd, resize_buf);
            if (src_rga_buffer_handle) releasebuffer_handle(src_rga_buffer_handle);
            if (src_buf) dma_buf_free(src_buf_size, &src_dma_fd, src_buf);
            return cv::Mat();
        }
        memset(padding_buf, 0x00, padding_buf_size);

        padding_rga_buffer_handle = importbuffer_fd(padding_dma_fd, padding_buf_size);
        if (!padding_rga_buffer_handle) 
        { 
            if (padding_buf) dma_buf_free(padding_buf_size, &padding_dma_fd, padding_buf);
            if (resize_rga_buffer_handle) releasebuffer_handle(resize_rga_buffer_handle);
            if (resize_buf) dma_buf_free(resize_buf_size, &resize_dma_fd, resize_buf);
            if (src_rga_buffer_handle) releasebuffer_handle(src_rga_buffer_handle);
            if (src_buf) dma_buf_free(src_buf_size, &src_dma_fd, src_buf);
            return cv::Mat();
        }

        padding_image = wrapbuffer_handle(padding_rga_buffer_handle, padding_virtual_width, padding_height, src_format);
        // padding_image.wstride = padding_virtual_width;
        // padding_image.hstride = padding_height;
        // padding_image.width   = src_virtual_width + 2 * padding_x;
        // padding_image.height  = src_height;

        int padding_left = 0;
        int padding_right = 0;
        int paddings = padding_image.width - src_image.width;
        if (paddings % 2 == 0)
        {
            padding_left = paddings / 2;
            padding_right = padding_left;
        }
        else
        {
            padding_left = paddings / 2;
            padding_right = padding_left + 1;
        }
        ret = immakeBorder(src_image, padding_image, 0, 0, padding_left, padding_right, IM_BORDER_CONSTANT, pad_color);
    }

    if (ret != IM_STATUS_SUCCESS)
    {
        printf("Error: rga_letterbox, immakeBorder failed: %s\n", imStrError((IM_STATUS)ret));
        if (padding_rga_buffer_handle) releasebuffer_handle(padding_rga_buffer_handle);
        if (padding_buf) dma_buf_free(padding_buf_size, &padding_dma_fd, padding_buf);
        if (resize_rga_buffer_handle) releasebuffer_handle(resize_rga_buffer_handle);
        if (resize_buf) dma_buf_free(resize_buf_size, &resize_dma_fd, resize_buf);
        if (src_rga_buffer_handle) releasebuffer_handle(src_rga_buffer_handle);
        if (src_buf) dma_buf_free(src_buf_size, &src_dma_fd, src_buf);
        return cv::Mat();
    }

    // 保存参数
    letterbox_param.x_pad = padding_x;
    letterbox_param.y_pad = padding_y;
    letterbox_param.scale = scale;

    ret = imresize(padding_image, resize_image);
    if (ret != IM_STATUS_SUCCESS)
    {
        printf("Error: rga_letterbox, imresize failed: %s\n", imStrError((IM_STATUS)ret));
        if (padding_rga_buffer_handle) releasebuffer_handle(padding_rga_buffer_handle);
        if (padding_buf) dma_buf_free(padding_buf_size, &padding_dma_fd, padding_buf);
        if (resize_rga_buffer_handle) releasebuffer_handle(resize_rga_buffer_handle);
        if (resize_buf) dma_buf_free(resize_buf_size, &resize_dma_fd, resize_buf);
        if (src_rga_buffer_handle) releasebuffer_handle(src_rga_buffer_handle);
        if (src_buf) dma_buf_free(src_buf_size, &src_dma_fd, src_buf);
        return cv::Mat();
    }

    cv::Mat final_result = cv::Mat(dst_height, dst_width, CV_8UC3, resize_buf).clone();

    // --- clean ---
    if (padding_rga_buffer_handle) releasebuffer_handle(padding_rga_buffer_handle);
    if (padding_buf) dma_buf_free(padding_buf_size, &padding_dma_fd, padding_buf);
    if (resize_rga_buffer_handle) releasebuffer_handle(resize_rga_buffer_handle);
    if (resize_buf) dma_buf_free(resize_buf_size, &resize_dma_fd, resize_buf);
    if (src_rga_buffer_handle) releasebuffer_handle(src_rga_buffer_handle);
    if (src_buf) dma_buf_free(src_buf_size, &src_dma_fd, src_buf);

    return final_result;
}


int main()
{
    printf("%s\n", querystring(RGA_ALL));

    std::string test_image_file = "./1.jpg";
    cv::Mat src_image = cv::imread(test_image_file);
    if (src_image.data == nullptr)
    {
        printf("read image failed !\n");
        return -1;
    }

    // 创建 letterbox 参数结构
    LetterboxParam letterbox_param;

    cv::Mat padding_image = rga_letterbox(src_image, letterbox_param, 640, 384);
    if (padding_image.empty()) 
    {
        cerr << "padding operation failed!" << endl;
        return -1;;
    }

    std::string save_file_path = "1_result.jpg";
    cv::imwrite(save_file_path, padding_image);
 
    return 0;
}

原始图片:

letterbox到640x384: