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)
| 方法 | RESIZE | CROP |
|---|
| RGA | 1.82~2.12ms | 0.93~1.03ms |
| VGS | 0.83~0.93ms | 1.44~2.2ms |
| OPENCV + OPENCL(bgr图像) | 0.13ms | 0.15ms |
| OPENCV | 1.18ms | 0.51ms |