【RK3588】图像操作耗时评估

1. RGA CROP/RESIZE

CROP:


```c++
/*
 * Copyright (C) 2022  Rockchip Electronics Co., Ltd.
 * Authors:
 *     YuQiaowei <cerf.yu@rock-chips.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_NDEBUG 0
#undef LOG_TAG
#define LOG_TAG "rga_crop_demo"

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <linux/stddef.h>
#include <chrono>
#include "RgaUtils.h"
#include "im2d.hpp"

#include "utils.h"

// 默认参数
#define DEFAULT_SRC_WIDTH 1920
#define DEFAULT_SRC_HEIGHT 1080
#define DEFAULT_CROP_X 100
#define DEFAULT_CROP_Y 100
#define DEFAULT_CROP_WIDTH 200
#define DEFAULT_CROP_HEIGHT 200
#define DEFAULT_SRC_FORMAT RK_FORMAT_YCrCb_420_SP
#define DEFAULT_DST_FORMAT RK_FORMAT_YCrCb_420_SP

// 使用说明
void print_usage(const char* program_name) {
    printf("Usage: %s [options]\n", program_name);
    printf("Options:\n");
    printf("  -i <input_file>    输入YUV文件路径 (必需)\n");
    printf("  -o <output_file>   输出YUV文件路径 (默认: output_crop.yuv)\n");
    printf("  -w <width>         源图像宽度 (默认: %d)\n", DEFAULT_SRC_WIDTH);
    printf("  -h <height>        源图像高度 (默认: %d)\n", DEFAULT_SRC_HEIGHT);
    printf("  -x <crop_x>        裁剪起始X坐标 (默认: %d)\n", DEFAULT_CROP_X);
    printf("  -y <crop_y>        裁剪起始Y坐标 (默认: %d)\n", DEFAULT_CROP_Y);
    printf("  -c <crop_width>   裁剪宽度 (默认: %d)\n", DEFAULT_CROP_WIDTH);
    printf("  -r <crop_height>  裁剪高度 (默认: %d)\n", DEFAULT_CROP_HEIGHT);
    printf("  -f <format>        图像格式 (0:YUYV_420, 1:YUV420, 2:YUV422, 默认: 0)\n");
    printf("  -s                 显示支持的格式\n");
    printf("  --help             显示此帮助信息\n");
    printf("\n示例:\n");
    printf("  %s -i input.yuv -o output.yuv -w 1920 -h 1080 -x 100 -y 100 -c 200 -r 200\n", program_name);
    printf("  %s -i test.yuv -w 1280 -h 720 -x 50 -y 50 -c 640 -r 480\n", program_name);
}

// 显示支持的格式
void print_supported_formats() {
    printf("支持的图像格式:\n");
    printf("  0: RK_FORMAT_YUYV_420 (YUYV 4:2:0)\n");
    printf("  1: RK_FORMAT_YUV420 (YUV 4:2:0)\n");
    printf("  2: RK_FORMAT_YUV422 (YUV 4:2:2)\n");
    printf("  3: RK_FORMAT_RGB_888 (RGB 24位)\n");
    printf("  4: RK_FORMAT_RGBA_8888 (RGBA 32位)\n");
}

// 解析命令行参数
int parse_arguments(int argc, char* argv[], 
                   char** input_file, char** output_file,
                   int* src_width, int* src_height,
                   int* crop_x, int* crop_y, int* crop_width, int* crop_height,
                   int* src_format, int* dst_format) {
    
    // 设置默认值
    *input_file = NULL;
    *output_file = (char*)"output_crop.yuv";
    *src_width = DEFAULT_SRC_WIDTH;
    *src_height = DEFAULT_SRC_HEIGHT;
    *crop_x = DEFAULT_CROP_X;
    *crop_y = DEFAULT_CROP_Y;
    *crop_width = DEFAULT_CROP_WIDTH;
    *crop_height = DEFAULT_CROP_HEIGHT;
    *src_format = DEFAULT_SRC_FORMAT;
    *dst_format = DEFAULT_DST_FORMAT;
    
    int opt;
    while ((opt = getopt(argc, argv, "i:o:w:h:x:y:c:r:f:s")) != -1) {
        switch (opt) {
            case 'i':
                *input_file = optarg;
                break;
            case 'o':
                *output_file = optarg;
                break;
            case 'w':
                *src_width = atoi(optarg);
                if (*src_width <= 0) {
                    printf("错误: 宽度必须大于0\n");
                    return -1;
                }
                break;
            case 'h':
                *src_height = atoi(optarg);
                if (*src_height <= 0) {
                    printf("错误: 高度必须大于0\n");
                    return -1;
                }
                break;
            case 'x':
                *crop_x = atoi(optarg);
                if (*crop_x < 0) {
                    printf("错误: 裁剪X坐标不能为负数\n");
                    return -1;
                }
                break;
            case 'y':
                *crop_y = atoi(optarg);
                if (*crop_y < 0) {
                    printf("错误: 裁剪Y坐标不能为负数\n");
                    return -1;
                }
                break;
            case 'c':
                *crop_width = atoi(optarg);
                if (*crop_width <= 0) {
                    printf("错误: 裁剪宽度必须大于0\n");
                    return -1;
                }
                break;
            case 'r':
                *crop_height = atoi(optarg);
                if (*crop_height <= 0) {
                    printf("错误: 裁剪高度必须大于0\n");
                    return -1;
                }
                break;
            case 'f':
                {
                    int format = atoi(optarg);
                    switch (format) {
                        case 0: *src_format = RK_FORMAT_YCrCb_420_SP; break;
                        case 1: *src_format = RK_FORMAT_RGB_888; break;
                        case 2: *src_format = RK_FORMAT_RGBA_8888; break;
                        default:
                            printf("错误: 不支持的格式 %d\n", format);
                            return -1;
                    }
                    *dst_format = *src_format; // 输出格式与输入格式相同
                }
                break;
            case 's':
                print_supported_formats();
                return 1; // 特殊返回值,表示只显示格式信息
            case '?':
            default:
                return -1;
        }
    }
    
    // 检查必需参数
    if (*input_file == NULL) {
        printf("错误: 必须指定输入文件 (-i)\n");
        return -1;
    }
    
    // 验证裁剪参数
    if (*crop_x + *crop_width > *src_width) {
        printf("错误: 裁剪区域超出源图像宽度 (x=%d, width=%d, src_width=%d)\n", 
               *crop_x, *crop_width, *src_width);
        return -1;
    }
    
    if (*crop_y + *crop_height > *src_height) {
        printf("错误: 裁剪区域超出源图像高度 (y=%d, height=%d, src_height=%d)\n", 
               *crop_y, *crop_height, *src_height);
        return -1;
    }
    
    return 0;
}

// 读取YUV文件
int read_yuv_file(const char* filename, char* buffer, int width, int height, int format) {
    FILE* fp = fopen(filename, "rb");
    if (fp == NULL) {
        printf("错误: 无法打开文件 %s (%s)\n", filename, strerror(errno));
        return -1;
    }
    
    // 计算文件大小
    int bpp = get_bpp_from_format(format);
    int expected_size = width * height * bpp;
    printf("bpp: %d, expected_size: %d\n", bpp, expected_size);
    
    // 获取文件大小
    fseek(fp, 0, SEEK_END);
    long file_size = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    
    if (file_size < expected_size) {
        printf("错误: 文件大小不足 (期望: %d 字节, 实际: %ld 字节)\n", expected_size, file_size);
        fclose(fp);
        return -1;
    }
    
    // 读取数据
    size_t bytes_read = fread(buffer, 1, expected_size, fp);
    fclose(fp);
    
    if (bytes_read != expected_size) {
        printf("错误: 读取数据不完整 (期望: %d 字节, 实际: %zu 字节)\n", expected_size, bytes_read);
        return -1;
    }
    
    printf("成功读取YUV文件: %s (%zu 字节)\n", filename, bytes_read);
    return 0;
}

// 写入YUV文件
int write_yuv_file(const char* filename, char* buffer, int width, int height, int format) {
    FILE* fp = fopen(filename, "wb");
    if (fp == NULL) {
        printf("错误: 无法创建输出文件 %s (%s)\n", filename, strerror(errno));
        return -1;
    }
    
    int bpp = get_bpp_from_format(format);
    int size = width * height * bpp;
    
    size_t bytes_written = fwrite(buffer, 1, size, fp);
    fclose(fp);
    
    if (bytes_written != size) {
        printf("错误: 写入数据不完整 (期望: %d 字节, 实际: %zu 字节)\n", size, bytes_written);
        return -1;
    }
    
    printf("成功写入YUV文件: %s (%zu 字节)\n", filename, bytes_written);
    return 0;
}

int main(int argc, char* argv[]) {
    int ret = 0;
    char *input_file = NULL, *output_file = NULL;
    int src_width, src_height;
    int crop_x, crop_y, crop_width, crop_height;
    int src_format, dst_format;
    char *src_buf, *dst_buf;
    int src_buf_size, dst_buf_size;
    
    rga_buffer_t src_img, dst_img;
    im_rect rect;
    rga_buffer_handle_t src_handle, dst_handle;

    std::chrono::steady_clock::time_point start_time, end_time;
    std::chrono::duration<double> duration;

    // 解析命令行参数
    ret = parse_arguments(argc, argv, &input_file, &output_file,
                         &src_width, &src_height,
                         &crop_x, &crop_y, &crop_width, &crop_height,
                         &src_format, &dst_format);
    
    if (ret == 1) {
        // 只显示格式信息,正常退出
        return 0;
    } else if (ret != 0) {
        print_usage(argv[0]);
        return -1;
    }
    
    // 显示参数信息
    printf("=== RGA 裁剪演示程序 ===\n");
    printf("输入文件: %s\n", input_file);
    printf("输出文件: %s\n", output_file);
    printf("源图像尺寸: %dx%d\n", src_width, src_height);
    printf("裁剪区域: (%d, %d) -> (%dx%d)\n", crop_x, crop_y, crop_width, crop_height);
    printf("图像格式: %d\n", src_format);
    printf("\n");

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

    // 计算缓冲区大小
    src_buf_size = src_width * src_height * get_bpp_from_format(src_format);
    dst_buf_size = crop_width * crop_height * get_bpp_from_format(dst_format);

    src_buf = (char *)malloc(src_buf_size);
    dst_buf = (char *)malloc(dst_buf_size);
    
    if (src_buf == NULL || dst_buf == NULL) {
        printf("错误: 内存分配失败\n");
        goto release_buffer;
    }

    // 读取输入YUV文件
    if (read_yuv_file(input_file, src_buf, src_width, src_height, src_format) != 0) {
        printf("错误: 读取输入文件失败\n");
        goto release_buffer;
    }
    
    // 初始化目标缓冲区
    memset(dst_buf, 0x80, dst_buf_size);

    // 创建RGA缓冲区句柄
    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("错误: 创建RGA缓冲区句柄失败\n");
        goto release_buffer;
    }

    // 包装缓冲区
    src_img = wrapbuffer_handle(src_handle, src_width, src_height, src_format);
    dst_img = wrapbuffer_handle(dst_handle, crop_width, crop_height, dst_format);

    // 设置裁剪区域
    rect.x = crop_x;
    rect.y = crop_y;
    rect.width = crop_width;
    rect.height = crop_height;

    printf("开始裁剪处理...\n");
    
    // 检查参数
    ret = imcheck(src_img, dst_img, {}, {});
    if (IM_STATUS_NOERROR != ret) {
        printf("错误: 参数检查失败, %s\n", imStrError((IM_STATUS)ret));
        goto release_buffer;
    }

    // 执行裁剪
    // 执行100次,统计单次耗时
    start_time = std::chrono::steady_clock::now();
    for (int i = 0; i < 100; i++) {
        ret = imcrop(src_img, dst_img, rect);
    }
    end_time = std::chrono::steady_clock::now();
    duration = end_time - start_time;
    printf("单次裁剪耗时: %.2f ms\n", duration.count() * 1000 / 100);

    ret = imcrop(src_img, dst_img, rect);
    if (ret == IM_STATUS_SUCCESS) {
        printf("裁剪处理成功!\n");
        
        // 写入输出文件
        if (write_yuv_file(output_file, dst_buf, crop_width, crop_height, dst_format) == 0) {
            printf("输出文件保存成功: %s\n", output_file);
        } else {
            printf("错误: 保存输出文件失败\n");
            ret = -1;
        }
    } else {
        printf("错误: 裁剪处理失败, %s\n", imStrError((IM_STATUS)ret));
    }

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;
}

RESIZE:

/*
 * Copyright (C) 2022  Rockchip Electronics Co., Ltd.
 * Authors:
 *     YuQiaowei <cerf.yu@rock-chips.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_NDEBUG 0
#undef LOG_TAG
#define LOG_TAG "rga_resize_demo"

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <linux/stddef.h>
#include <chrono>

#include "RgaUtils.h"
#include "im2d.hpp"

#include "utils.h"

// 默认参数
#define DEFAULT_SRC_WIDTH 1280
#define DEFAULT_SRC_HEIGHT 720
#define DEFAULT_DST_WIDTH 1920
#define DEFAULT_DST_HEIGHT 1080
#define DEFAULT_SRC_FORMAT RK_FORMAT_RGBA_8888
#define DEFAULT_DST_FORMAT RK_FORMAT_RGBA_8888

// 使用说明
void print_usage(const char* program_name) {
    printf("Usage: %s [options]\n", program_name);
    printf("Options:\n");
    printf("  -i <input_file>    输入文件路径 (必需)\n");
    printf("  -o <output_file>   输出文件路径 (默认: output_resize.yuv)\n");
    printf("  -w <src_width>     源图像宽度 (默认: %d)\n", DEFAULT_SRC_WIDTH);
    printf("  -h <src_height>    源图像高度 (默认: %d)\n", DEFAULT_SRC_HEIGHT);
    printf("  -W <dst_width>     目标图像宽度 (默认: %d)\n", DEFAULT_DST_WIDTH);
    printf("  -H <dst_height>    目标图像高度 (默认: %d)\n", DEFAULT_DST_HEIGHT);
    printf("  -f <format>        图像格式 (0:RGBA_8888, 1:RGB_888, 2:YCrCb_420_SP, 默认: 0)\n");
    printf("  -t <times>         测试次数 (默认: 100)\n");
    printf("  -s                 显示支持的格式\n");
    printf("  --help             显示此帮助信息\n");
    printf("\n示例:\n");
    printf("  %s -i input.yuv -o output.yuv -w 1280 -h 720 -W 1920 -H 1080\n", program_name);
    printf("  %s -i test.yuv -w 640 -h 480 -W 1280 -H 720 -t 200\n", program_name);
}

// 显示支持的格式
void print_supported_formats() {
    printf("支持的图像格式:\n");
    printf("  0: RK_FORMAT_RGBA_8888 (RGBA 32位)\n");
    printf("  1: RK_FORMAT_RGB_888 (RGB 24位)\n");
    printf("  2: RK_FORMAT_YCrCb_420_SP (YUV 4:2:0)\n");
    printf("  3: RK_FORMAT_YUYV_420 (YUYV 4:2:0)\n");
    printf("  4: RK_FORMAT_YUV420 (YUV 4:2:0)\n");
}

// 解析命令行参数
int parse_arguments(int argc, char* argv[], 
                   char** input_file, char** output_file,
                   int* src_width, int* src_height,
                   int* dst_width, int* dst_height,
                   int* src_format, int* dst_format,
                   int* test_times) {
    
    // 设置默认值
    *input_file = NULL;
    *output_file = (char*)"output_resize.yuv";
    *src_width = DEFAULT_SRC_WIDTH;
    *src_height = DEFAULT_SRC_HEIGHT;
    *dst_width = DEFAULT_DST_WIDTH;
    *dst_height = DEFAULT_DST_HEIGHT;
    *src_format = DEFAULT_SRC_FORMAT;
    *dst_format = DEFAULT_DST_FORMAT;
    *test_times = 100;
    
    int opt;
    while ((opt = getopt(argc, argv, "i:o:w:h:W:H:f:t:s")) != -1) {
        switch (opt) {
            case 'i':
                *input_file = optarg;
                break;
            case 'o':
                *output_file = optarg;
                break;
            case 'w':
                *src_width = atoi(optarg);
                if (*src_width <= 0) {
                    printf("错误: 源图像宽度必须大于0\n");
                    return -1;
                }
                break;
            case 'h':
                *src_height = atoi(optarg);
                if (*src_height <= 0) {
                    printf("错误: 源图像高度必须大于0\n");
                    return -1;
                }
                break;
            case 'W':
                *dst_width = atoi(optarg);
                if (*dst_width <= 0) {
                    printf("错误: 目标图像宽度必须大于0\n");
                    return -1;
                }
                break;
            case 'H':
                *dst_height = atoi(optarg);
                if (*dst_height <= 0) {
                    printf("错误: 目标图像高度必须大于0\n");
                    return -1;
                }
                break;
            case 'f':
                {
                    int format = atoi(optarg);
                    switch (format) {
                        case 0: *src_format = RK_FORMAT_RGBA_8888; break;
                        case 1: *src_format = RK_FORMAT_RGB_888; break;
                        case 2: *src_format = RK_FORMAT_YCrCb_420_SP; break;
                        case 3: *src_format = RK_FORMAT_YUYV_420; break;
                        default:
                            printf("错误: 不支持的格式 %d\n", format);
                            return -1;
                    }
                    *dst_format = *src_format; // 输出格式与输入格式相同
                }
                break;
            case 't':
                *test_times = atoi(optarg);
                if (*test_times <= 0) {
                    printf("错误: 测试次数必须大于0\n");
                    return -1;
                }
                break;
            case 's':
                print_supported_formats();
                return 1; // 特殊返回值,表示只显示格式信息
            case '?':
            default:
                return -1;
        }
    }
    
    // 检查必需参数
    if (*input_file == NULL) {
        printf("错误: 必须指定输入文件 (-i)\n");
        return -1;
    }
    
    return 0;
}

// 读取文件
int read_image_file(const char* filename, char* buffer, int width, int height, int format) {
    FILE* fp = fopen(filename, "rb");
    if (fp == NULL) {
        printf("错误: 无法打开文件 %s (%s)\n", filename, strerror(errno));
        return -1;
    }
    
    // 计算文件大小
    int bpp = get_bpp_from_format(format);
    int expected_size = width * height * bpp;
    printf("bpp: %d, expected_size: %d\n", bpp, expected_size);
    
    // 获取文件大小
    fseek(fp, 0, SEEK_END);
    long file_size = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    
    if (file_size < expected_size) {
        printf("错误: 文件大小不足 (期望: %d 字节, 实际: %ld 字节)\n", expected_size, file_size);
        fclose(fp);
        return -1;
    }
    
    // 读取数据
    size_t bytes_read = fread(buffer, 1, expected_size, fp);
    fclose(fp);
    
    if (bytes_read != expected_size) {
        printf("错误: 读取数据不完整 (期望: %d 字节, 实际: %zu 字节)\n", expected_size, bytes_read);
        return -1;
    }
    
    printf("成功读取文件: %s (%zu 字节)\n", filename, bytes_read);
    return 0;
}

// 写入文件
int write_image_file(const char* filename, char* buffer, int width, int height, int format) {
    FILE* fp = fopen(filename, "wb");
    if (fp == NULL) {
        printf("错误: 无法创建输出文件 %s (%s)\n", filename, strerror(errno));
        return -1;
    }
    
    int bpp = get_bpp_from_format(format);
    int size = width * height * bpp;
    
    size_t bytes_written = fwrite(buffer, 1, size, fp);
    fclose(fp);
    
    if (bytes_written != size) {
        printf("错误: 写入数据不完整 (期望: %d 字节, 实际: %zu 字节)\n", size, bytes_written);
        return -1;
    }
    
    printf("成功写入文件: %s (%zu 字节)\n", filename, bytes_written);
    return 0;
}

int main(int argc, char* argv[]) {
    int ret = 0;
    char *input_file = NULL, *output_file = NULL;
    int src_width, src_height;
    int dst_width, dst_height;
    int src_format, dst_format;
    int test_times;
    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;

    std::chrono::steady_clock::time_point start_time, end_time;
    std::chrono::duration<double> duration;

    // 解析命令行参数
    ret = parse_arguments(argc, argv, &input_file, &output_file,
                         &src_width, &src_height, &dst_width, &dst_height,
                         &src_format, &dst_format, &test_times);
    
    if (ret == 1) {
        // 只显示格式信息,正常退出
        return 0;
    } else if (ret != 0) {
        print_usage(argv[0]);
        return -1;
    }
    
    // 显示参数信息
    printf("=== RGA 缩放演示程序 ===\n");
    printf("输入文件: %s\n", input_file);
    printf("输出文件: %s\n", output_file);
    printf("源图像尺寸: %dx%d\n", src_width, src_height);
    printf("目标图像尺寸: %dx%d\n", dst_width, dst_height);
    printf("图像格式: %d\n", src_format);
    printf("测试次数: %d\n", test_times);
    printf("\n");

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

    // 计算缓冲区大小
    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);
    
    if (src_buf == NULL || dst_buf == NULL) {
        printf("错误: 内存分配失败\n");
        goto release_buffer;
    }

    // 读取输入文件
    if (read_image_file(input_file, src_buf, src_width, src_height, src_format) != 0) {
        printf("错误: 读取输入文件失败\n");
        goto release_buffer;
    }
    
    // 初始化目标缓冲区
    memset(dst_buf, 0x80, dst_buf_size);

    // 创建RGA缓冲区句柄
    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("错误: 创建RGA缓冲区句柄失败\n");
        goto release_buffer;
    }

    // 包装缓冲区
    src_img = wrapbuffer_handle(src_handle, src_width, src_height, src_format);
    dst_img = wrapbuffer_handle(dst_handle, dst_width, dst_height, dst_format);

    printf("开始缩放处理...\n");
    
    // 检查参数
    ret = imcheck(src_img, dst_img, {}, {});
    if (IM_STATUS_NOERROR != ret) {
        printf("错误: 参数检查失败, %s\n", imStrError((IM_STATUS)ret));
        goto release_buffer;
    }

    // 执行缩放并测试耗时
    start_time = std::chrono::steady_clock::now();
    for (int i = 0; i < test_times; i++) {
        ret = imresize(src_img, dst_img);
    }
    end_time = std::chrono::steady_clock::now();
    duration = end_time - start_time;
    
    printf("缩放处理成功!\n");
    printf("总耗时: %.2f ms\n", duration.count() * 1000);
    printf("平均单次耗时: %.2f ms\n", duration.count() * 1000 / test_times);
    printf("处理速度: %.2f FPS\n", test_times / duration.count());
    
    // 写入输出文件
    if (write_image_file(output_file, dst_buf, dst_width, dst_height, dst_format) == 0) {
        printf("输出文件保存成功: %s\n", output_file);
    } else {
        printf("错误: 保存输出文件失败\n");
        ret = -1;
    }

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;
}

VGS CROP/RESIZE

#ifndef _VIDEO_VGS_H_
#define _VIDEO_VGS_H_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "rk_comm_rgn.h"
#include "rk_comm_vi.h"
#include "rk_comm_vo.h"
#include "rk_common.h"
#include "rk_debug.h"
#include "rk_defines.h"
#include "rk_mpi_cal.h"
#include "rk_mpi_mb.h"
#include "rk_mpi_mmz.h"
#include "rk_mpi_rgn.h"
#include "rk_mpi_sys.h"
#include "rk_mpi_venc.h"
#include "rk_mpi_vi.h"
#include "rk_mpi_vo.h"
#include "rk_mpi_vpss.h"
#include "video_decode.h"

#include "rk_mpi_vdec.h"
#include "self_thread.h"
#include "stream_struct.h"

#include "rk_mpi_vgs.h"

class RKVgs {
public:
  RKVgs();
  ~RKVgs();
  int startJob();
  int endJob();
  static int destroyFrame(VIDEO_FRAME_INFO_S *frame);
  static int createFrame(int height, int width, PIXEL_FORMAT_E fmt,
                         VIDEO_FRAME_INFO_S *frame);
  int crop(VIDEO_FRAME_INFO_S &src, VIDEO_FRAME_INFO_S &dst, int x, int y,
           int w, int h);
  int resize(VIDEO_FRAME_INFO_S &src, VIDEO_FRAME_INFO_S &dst,
             VGS_SCLCOEF_MODE_E scale_mode);
  int rotate(VIDEO_FRAME_INFO_S &src, VIDEO_FRAME_INFO_S &dst,
             ROTATION_E rotate_mode);
private:
  VGS_HANDLE m_handle;
  VGS_TASK_ATTR_S m_task;
};

#endif
```c++

```c++
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "rk_comm_rgn.h"
#include "rk_comm_vi.h"
#include "rk_comm_vo.h"
#include "rk_common.h"
#include "rk_debug.h"
#include "rk_defines.h"
#include "rk_mpi_cal.h"
#include "rk_mpi_mb.h"
#include "rk_mpi_mmz.h"
#include "rk_mpi_rgn.h"
#include "rk_mpi_sys.h"
#include "rk_mpi_venc.h"
#include "rk_mpi_vi.h"
#include "rk_mpi_vo.h"
#include "rk_mpi_vpss.h"
#include "video_decode.h"

#include "rk_mpi_vdec.h"
#include "self_thread.h"
#include "stream_struct.h"

// #define USE_OPENCV

#include "rk_mpi_vgs.h"
#include "video_vgs.h"

RKVgs::RKVgs() {}
RKVgs::~RKVgs() {}

int RKVgs::startJob() {
  int ret;
  ret = RK_MPI_VGS_BeginJob(&m_handle);
  CHECK_ERROR(ret, "RK_MPI_VGS_BeginJob failed = [%x]\n", ret);
  return ret;
}

int RKVgs::crop(VIDEO_FRAME_INFO_S &src, VIDEO_FRAME_INFO_S &dst, int x, int y,
                int w, int h) {
  int ret;
  m_task.stImgIn = src;
  m_task.stImgOut = dst;

  VGS_CROP_INFO_S crop_info;
  crop_info.enCropCoordinate = VGS_CROP_ABS_COOR;
  crop_info.stCropRect = {x, y, w, h};

  ret = startJob();
  CHECK_ERROR(ret, "startJob failed = [%x]\n", ret);

  ret = RK_MPI_VGS_AddCropTask(m_handle, &m_task, &crop_info);
  CHECK_GOTO(ret, EXIT, "RK_MPI_VGS_AddCropTask failed = [%x]\n", ret);

  ret = endJob();
  CHECK_ERROR(ret, "endJob failed = [%x]\n", ret);
  return 0;

EXIT:
  RK_MPI_VGS_CancelJob(m_handle);
  return ret;
}

int RKVgs::endJob() {
  int ret;
  ret = RK_MPI_VGS_EndJob(m_handle);
  CHECK_GOTO(ret, EXIT, "RK_MPI_VGS_EndJob failed = [%x]\n", ret);
  return 0;

EXIT:
  RK_MPI_VGS_CancelJob(m_handle);
  return ret;
}

int RKVgs::destroyFrame(VIDEO_FRAME_INFO_S *frame) {
  return RK_MPI_MB_ReleaseMB(frame->stVFrame.pMbBlk);
}

int RKVgs::createFrame(int height, int width, PIXEL_FORMAT_E fmt,
                       VIDEO_FRAME_INFO_S *frame) {
  int ret;
  PIC_BUF_ATTR_S pic_buf;
  MB_PIC_CAL_S pic_cal;
  memset(&pic_buf, 0, sizeof(PIC_BUF_ATTR_S));

  pic_buf.u32Width = width;
  pic_buf.u32Height = height;
  pic_buf.enPixelFormat = fmt;
  pic_buf.enCompMode = COMPRESS_MODE_NONE;

  ret = RK_MPI_CAL_VGS_GetPicBufferSize(&pic_buf, &pic_cal);
  CHECK_ERROR(ret, "VGS get pic size\n");

  ret = RK_MPI_SYS_MmzAlloc_Cached(&(frame->stVFrame.pMbBlk), RK_NULL, RK_NULL,
                                   pic_cal.u32MBSize);
  CHECK_ERROR(ret, "RK_MPI_SYS_MmzAlloc_Cached\n");
  RK_MPI_SYS_MmzFlushCache(frame->stVFrame.pMbBlk, RK_TRUE);

  // memcpy(temp_data, dst_frame.stVFrame.pVirAddr[0],
  // dst_frame.stVFrame.u32VirHeight * dst_frame.stVFrame.u32VirWidth);
  unsigned char *blk_vir =
      (unsigned char *)RK_MPI_MB_Handle2VirAddr(frame->stVFrame.pMbBlk);

  PRINTF("w h = [%d %d] stride w h = [%d %d]\n", width, height,
         pic_cal.u32VirWidth, pic_cal.u32VirHeight);
  frame->stVFrame.u32Width = pic_buf.u32Width;
  frame->stVFrame.u32Height = pic_buf.u32Height;
  frame->stVFrame.u32VirWidth = pic_cal.u32VirWidth;
  frame->stVFrame.u32VirHeight = pic_cal.u32VirHeight;
  frame->stVFrame.enPixelFormat = pic_buf.enPixelFormat;
  frame->stVFrame.enCompressMode = pic_buf.enCompMode;
  frame->stVFrame.pVirAddr[0] = (void *)blk_vir;
  frame->stVFrame.pVirAddr[1] =
      (void *)(blk_vir + pic_cal.u32VirWidth * pic_cal.u32VirHeight);
  return RK_SUCCESS;
}

int RKVgs::resize(VIDEO_FRAME_INFO_S &src, VIDEO_FRAME_INFO_S &dst,
                  VGS_SCLCOEF_MODE_E scale_mode) {
  int ret;
  m_task.stImgIn = src;
  m_task.stImgOut = dst;

  ret = startJob();
  CHECK_ERROR(ret, "startJob failed = [%x]\n", ret);

  ret = RK_MPI_VGS_AddScaleTask(m_handle, &m_task, scale_mode);
  CHECK_GOTO(ret, EXIT, "RK_MPI_VGS_AddScaleTask failed = [%x]\n", ret);

  ret = endJob();
  CHECK_ERROR(ret, "endJob failed = [%x]\n", ret);
  return 0;

EXIT:
  RK_MPI_VGS_CancelJob(m_handle);
  return ret;
}

int RKVgs::rotate(VIDEO_FRAME_INFO_S &src, VIDEO_FRAME_INFO_S &dst,
                  ROTATION_E rotate_mode) {
  int ret;
  m_task.stImgIn = src;
  m_task.stImgOut = dst;

  ret = startJob();
  CHECK_ERROR(ret, "startJob failed = [%x]\n", ret);

  ret = RK_MPI_VGS_AddRotationTask(m_handle, &m_task, rotate_mode);
  CHECK_GOTO(ret, EXIT, "RK_MPI_VGS_AddRotationTask failed = [%x]\n", ret);
  ret = endJob();
  CHECK_ERROR(ret, "endJob failed = [%x]\n", ret);
  return 0;
EXIT:
  RK_MPI_VGS_CancelJob(m_handle);
  return ret;
}
#include "video_vgs.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <chrono>

static void dump_yuv_nv12(const VIDEO_FRAME_INFO_S &frame, const char *filename) {
  FILE *fp = fopen(filename, "wb");
  if (!fp) {
    printf("open %s failed\n", filename);
    return;
  }
  RK_U32 y_size = frame.stVFrame.u32VirWidth * frame.stVFrame.u32VirHeight;
  RK_U32 uv_size = y_size / 2;
  fwrite(frame.stVFrame.pVirAddr[0], 1, y_size, fp);
  fwrite(frame.stVFrame.pVirAddr[1], 1, uv_size, fp);
  fclose(fp);
}

static int load_yuv_nv12_to_frame(const char *filename, int src_w, int src_h, VIDEO_FRAME_INFO_S &frame) {
  FILE *fp = fopen(filename, "rb");
  if (!fp) {
    printf("open %s failed\n", filename);
    return -1;
  }
  RK_U32 vir_w = frame.stVFrame.u32VirWidth;
  RK_U32 vir_h = frame.stVFrame.u32VirHeight;
  unsigned char *y = (unsigned char *)frame.stVFrame.pVirAddr[0];
  unsigned char *uv = (unsigned char *)frame.stVFrame.pVirAddr[1];

  // Y plane: src_h rows, each src_w bytes, dest stride vir_w
  for (int r = 0; r < src_h; ++r) {
    size_t rd = fread(y + r * vir_w, 1, src_w, fp);
    if (rd != (size_t)src_w) {
      fclose(fp);
      return -2;
    }
    // skip remaining stride if any is not needed (we already write at stride pos)
  }
  // If vir_h > src_h, zero the padding rows
  if ((int)vir_h > src_h) {
    memset(y + src_h * vir_w, 0, (vir_h - src_h) * vir_w);
  }

  // UV plane: (src_h/2) rows, each src_w bytes, dest stride vir_w
  for (int r = 0; r < src_h / 2; ++r) {
    size_t rd = fread(uv + r * vir_w, 1, src_w, fp);
    if (rd != (size_t)src_w) {
      fclose(fp);
      return -3;
    }
  }
  if ((int)(vir_h / 2) > (src_h / 2)) {
    memset(uv + (src_h / 2) * vir_w, 0, ((vir_h / 2) - (src_h / 2)) * vir_w);
  }

  fclose(fp);
  return 0;
}

static int test_crop(VIDEO_FRAME_INFO_S &src_frame, int crop_x, int crop_y, int crop_w, int crop_h) {
  printf("=== Testing Crop ===\n");
  printf("Crop: (%d, %d) -> (%d, %d) size: %dx%d\n", crop_x, crop_y, crop_x + crop_w, crop_y + crop_h, crop_w, crop_h);
  
  VIDEO_FRAME_INFO_S crop_frame;
  memset(&crop_frame, 0, sizeof(crop_frame));
  int ret = RKVgs::createFrame(crop_h, crop_w, RK_FMT_YUV420SP, &crop_frame);
  CHECK_ERROR(ret, "create crop_frame failed = [%x]\n", ret);

  RKVgs vgs;
  
  // 连续执行100次 统计时间
  std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
  for (int i = 0; i < 100; i++) {
    ret = vgs.crop(src_frame, crop_frame, crop_x, crop_y, crop_w, crop_h);
    CHECK_ERROR(ret, "vgs.crop failed = [%x]\n", ret);
  }
  std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
  printf("crop time = [%f]ms\n", std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() / 100.0);
  
  dump_yuv_nv12(crop_frame, "vgs_crop.yuv");
  printf("Crop result saved to: vgs_crop.yuv\n");
  
  RKVgs::destroyFrame(&crop_frame);
  return 0;
}

static int test_resize(VIDEO_FRAME_INFO_S &src_frame, int dst_w, int dst_h) {
  printf("=== Testing Resize ===\n");
  printf("Resize: %dx%d -> %dx%d\n", src_frame.stVFrame.u32Width, src_frame.stVFrame.u32Height, dst_w, dst_h);
  
  VIDEO_FRAME_INFO_S resize_frame;
  memset(&resize_frame, 0, sizeof(resize_frame));
  int ret = RKVgs::createFrame(dst_h, dst_w, RK_FMT_YUV420SP, &resize_frame);
  CHECK_ERROR(ret, "create resize_frame failed = [%x]\n", ret);

  RKVgs vgs;
  
  // 连续执行100次 统计时间
  std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
  for (int i = 0; i < 100; i++) {
    ret = vgs.resize(src_frame, resize_frame, VGS_SCLCOEF_TAP8);
    CHECK_ERROR(ret, "vgs.resize failed = [%x]\n", ret);
  }
  std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
  printf("resize time = [%f]ms\n", std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() / 100.0);
  
  dump_yuv_nv12(resize_frame, "vgs_resize.yuv");
  printf("Resize result saved to: vgs_resize.yuv\n");
  
  RKVgs::destroyFrame(&resize_frame);
  return 0;
}

static void usage(const char *prog) {
  printf("Usage: %s <input_nv12> <src_w> <src_h> [crop_x crop_y crop_w crop_h dst_w dst_h]\n", prog);
  printf("  Example: %s in.yuv 1920 1080 320 180 1280 720 640 360\n", prog);
}

int main(int argc, char *argv[]) {
  if (argc != 4 && argc != 10) {
    usage(argv[0]);
    return -1;
  }

  const char *in_path = argv[1];
  int src_w = atoi(argv[2]);
  int src_h = atoi(argv[3]);

  int crop_x = 320, crop_y = 180, crop_w = 1280, crop_h = 720;
  int dst_w = 640, dst_h = 360;

  if (argc == 10) {
    crop_x = atoi(argv[4]);
    crop_y = atoi(argv[5]);
    crop_w = atoi(argv[6]);
    crop_h = atoi(argv[7]);
    dst_w = atoi(argv[8]);
    dst_h = atoi(argv[9]);
  } else {
    // 默认基于给定源分辨率生成一组合理的裁剪与缩放参数
    crop_x = src_w / 6;
    crop_y = src_h / 6;
    crop_w = src_w * 2 / 3;
    crop_h = src_h * 2 / 3;
    dst_w = crop_w / 2;
    dst_h = crop_h / 2;
  }

  int ret = RK_MPI_SYS_Init();
  CHECK_ERROR(ret, "RK_MPI_SYS_Init failed = [%x]\n", ret);

  VIDEO_FRAME_INFO_S src_frame;
  memset(&src_frame, 0, sizeof(src_frame));
  ret = RKVgs::createFrame(src_h, src_w, RK_FMT_YUV420SP, &src_frame);
  CHECK_ERROR(ret, "create src_frame failed = [%x]\n", ret);

  ret = load_yuv_nv12_to_frame(in_path, src_w, src_h, src_frame);
  CHECK_ERROR(ret, "load_yuv_nv12_to_frame failed = [%x]\n", ret);
  dump_yuv_nv12(src_frame, "vgs_src.yuv");
  printf("Source loaded and saved to: vgs_src.yuv\n");

  // 测试裁剪
  ret = test_crop(src_frame, crop_x, crop_y, crop_w, crop_h);
  CHECK_ERROR(ret, "test_crop failed = [%x]\n", ret);

  // 测试缩放
  ret = test_resize(src_frame, dst_w, dst_h);
  CHECK_ERROR(ret, "test_resize failed = [%x]\n", ret);

  // 资源释放
  RKVgs::destroyFrame(&src_frame);

  ret = RK_MPI_SYS_Exit();
  CHECK_ERROR(ret, "RK_MPI_SYS_Exit failed = [%x]\n", ret);
  return 0;
}

OPENCV CROP/RESIZE 开启maligpu支持

rpdzkj@ubuntu2404:~/workspace/LxStream/rockit_video/workspace/bin$ ./test_opencv ../../../../LxProj/multi_camera_real_time_stitch/workspace/rk3588/data/dong/8x1/input_image/image_data0/vpss_0.jpg 0 0 1280 720 640 360
=== OpenCL Support Check ===
arm_release_ver: g13p0-01eac0, rk_so_ver: 10
OpenCL is available
OpenCL enabled: true
OpenCL devices: 1
  Device 0: Mali-G610 r0p0
Input image: 1920x1080
Source image saved to: opencv_src.jpg
=== Testing Single Operations ===
Single crop time = [1] us
Single resize time = [514] us
=== Testing OpenCV Crop ===
Crop: (0, 0) -> (1280, 720) size: 1280x720
OpenCV crop time = [0.570140]ms (total: 57014 us)
OpenCV crop result saved to: opencv_crop.jpg
=== Testing OpenCV Resize ===
Resize: 1920x1080 -> 640x360
OpenCV resize time = [0.132910]ms (total: 13291 us)
OpenCV resize result saved to: opencv_resize.jpg
=== OpenCV Tests Completed ===

OPENCV CROP/RESIZE 不开启maligpu支持

rpdzkj@ubuntu2404:~/workspace/LxStream/rockit_video/workspace/bin$ ./test_opencv ../../../../LxProj/multi_camera_real_time_stitch/workspace/rk3588/data/dong/8x1/input_image/image_data0/vpss_0.jpg 0 0 1280 720 640 360
=== OpenCL Support Check ===
arm_release_ver: g13p0-01eac0, rk_so_ver: 10
OpenCL is available
OpenCL enabled: true
OpenCL devices: 1
  Device 0: Mali-G610 r0p0
Input image: 1920x1080
Source image saved to: opencv_src.jpg
=== Testing Single Operations ===
Single crop time = [1351] us
Single resize time = [331805] us
=== Testing OpenCV Crop ===
Crop: (0, 0) -> (1280, 720) size: 1280x720
OpenCV crop time = [0.518540]ms (total: 51854 us)
OpenCV crop result saved to: opencv_crop.jpg
=== Testing OpenCV Resize ===
Resize: 1920x1080 -> 640x360
OpenCV resize time = [1.180260]ms (total: 118026 us)
OpenCV resize result saved to: opencv_resize.jpg
=== OpenCV Tests Completed ===

耗时统计(输入1080p, resize 640 * 360,crop 1280 * 720)

方法RESIZECROP
RGA1.82~2.12ms0.93~1.03ms
VGS0.83~0.93ms1.44~2.2ms
OPENCV + OPENCL(bgr图像)0.13ms0.15ms
OPENCV1.18ms0.51ms
### 如何在 RK3588 上部署 MobileViT 模型 #### 准备环境 为了成功地在 RK3588 平台上部署 MobileViT 模型,需要先设置好开发环境。这通常涉及到安装必要的依赖库以及配置硬件驱动程序。 - 安装操作系统并更新至最新版本。 - 配置 Rockchip 提供的 SDK 或者使用官方支持的操作系统镜像文件[^1]。 #### 获取预训练模型 下载预先训练好的 MobileViT 权重文件。可以从原始作者发布的资源链接获取这些权重,也可以通过 PyTorch Hub 等渠道获得经过验证的模型实例。 ```bash wget https://github.com/apple/ml-mobilevit/releases/download/v1.0/mobilevit_s.pth.tar ``` #### 转换模型格式 由于 RK3588 可能不直接支持某些框架下的原生模型格式,因此可能需要转换成适合目标平台推理引擎使用的格式,比如 ONNX 或 TensorRT。 ```python import torch from mobilevit import mobile_vit_small # 假设这是加载 MobileViT 的方式 model = mobile_vit_small() checkpoint = torch.load('mobilevit_s.pth.tar', map_location='cpu') model.load_state_dict(checkpoint['state_dict']) dummy_input = torch.randn(1, 3, 256, 256) torch.onnx.export( model, dummy_input, "mobilevit_s.onnx", opset_version=11, do_constant_folding=True, input_names=['input'], output_names=['output'] ) ``` #### 编译优化 利用 NPU(神经处理单元)加速器可以显著提升推断速度。Rockchip 自家提供了 rknn-toolkit 工具包用于编译和量化 ONNX 文件以便更好地适配其内部架构特性。 ```bash pip install rknn-toolkit2==1.7.0rc1 ``` 接着运行如下命令完成模型转制: ```bash rknn convert --onnx ./mobilevit_s.onnx \ --output ./mobilevit_s.rknn \ --target RK3588 \ --dataset /path/to/calibration/dataset.txt \ --mean_values "[123.675, 116.28 , 103.53]" \ --std_values "[58.395, 57.12 , 57.375]" ``` 这里 `--dataset` 参数指向了一个文本列表,其中每一行指定了一个图像路径用来做校准用途;而均值(`--mean_values`)与标准差(`--std_values`)则是根据 ImageNet 数据集统计得出,在实际应用时可根据具体情况调整。 #### 测试与评估 最后一步是在真实设备上测试已编译后的模型效果。编写简单的 Python 脚本调用 rknn-api 进行预测操作,并计算平均耗时作为性能指标之一。 ```python from rknn.api import RKNN # 创建 RKNN 对象 rknn = RKNN() ret = rknn.load_rknn('./mobilevit_s.rknn') if ret != 0: print('Load RKNN model failed!') exit(ret) # 初始化运行时环境 ret = rknn.init_runtime(target='rk3588') if ret != 0: print('Init runtime environment failed!') exit(ret) img = cv2.imread('/path/to/test/image.jpg') # 替换成自己的图片路径 outputs = rknn.inference(inputs=[img]) print(outputs) rknn.release() ``` 上述过程涵盖了从准备到执行的关键环节,具体细节可能会依据个人需求有所变化。值得注意的是,整个流程中涉及到了多个工具链的选择与集成,建议参考官方文档获取最准确的信息和支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值