RK356X rockit_mpi解析 (二)裁剪、覆盖、旋转、时间戳

本文详细描述了在使用RK_MPI时如何通过vpss模块实现图像裁剪和旋转功能,涉及初始化、配置参数和与RGN模块的交互,以及时间戳的实现方法。
摘要由CSDN通过智能技术生成

阅读RK_MPI的相关文档,裁剪缩放、旋转功能的实现依靠vpss模块

时间戳主要依靠RGN模块

如果编译vpss模块失败,要注意头文件要加上vpss模块的内容

#include "test_comm_vpss.h"
#include "../common/test_comm_vpss.cpp"

在test_vi_bind_vo_loop中

test_vi_init初始化vi后,创建vpss模块,并bind_vi_vpss

要使裁剪旋转等生效,要注意VpssChnMode,设置为VPSS_CHN_MODE_USER

    /* vpss */
    ctx->stVpssCfg.u32VpssChnCnt = 1;
    ctx->stVpssCfg.stGrpVpssAttr.u32MaxW = 4096;
    ctx->stVpssCfg.stGrpVpssAttr.u32MaxH = 4096;
    ctx->stVpssCfg.stGrpVpssAttr.enPixelFormat = RK_FMT_RGB888;
    ctx->stVpssCfg.stGrpVpssAttr.stFrameRate.s32SrcFrameRate = -1;
    ctx->stVpssCfg.stGrpVpssAttr.stFrameRate.s32DstFrameRate = -1;
    ctx->stVpssCfg.stGrpVpssAttr.enCompressMode = COMPRESS_MODE_NONE;
    for (i = 0; i < VPSS_MAX_CHN_NUM; i ++) {
        ctx->stVpssCfg.stVpssChnAttr[i].enChnMode = VPSS_CHN_MODE_USER;       //Enable crop and rotation, change mode to VPSS_CHN_MODE_USER
        ctx->stVpssCfg.stVpssChnAttr[i].enDynamicRange = DYNAMIC_RANGE_SDR8;
        ctx->stVpssCfg.stVpssChnAttr[i].enPixelFormat = RK_FMT_RGB888;
        ctx->stVpssCfg.stVpssChnAttr[i].stFrameRate.s32SrcFrameRate = -1;
        ctx->stVpssCfg.stVpssChnAttr[i].stFrameRate.s32DstFrameRate = -1;
        ctx->stVpssCfg.stVpssChnAttr[i].u32Width = ctx->width;
        ctx->stVpssCfg.stVpssChnAttr[i].u32Height = ctx->height;
        ctx->stVpssCfg.stVpssChnAttr[i].enCompressMode = COMPRESS_MODE_NONE;
    }

    // init vpss
    s32Ret = create_vpss(&ctx->stVpssCfg, 0, ctx->stVpssCfg.u32VpssChnCnt);
    if (s32Ret != RK_SUCCESS) {
        RK_LOGE("creat 0 grp vpss failed!");
        goto __FAILED;
    }

    MPP_CHN_S stViChn, stVpssChn;
    // bind vi to vpss
    stViChn.enModId    = RK_ID_VI;
    stViChn.s32DevId   = ctx->devId;
    stViChn.s32ChnId   = ctx->channelId;

    stVpssChn.enModId = RK_ID_VPSS;
    stVpssChn.s32DevId = 0;
    stVpssChn.s32ChnId = 0;

    RK_LOGD("vi to vpss ch %d vpss group %d", stVpssChn.s32ChnId , stVpssChn.s32DevId);
    s32Ret = RK_MPI_SYS_Bind(&stViChn, &stVpssChn);
    if (s32Ret != RK_SUCCESS) {
        RK_LOGE("vi and vpss bind error ");
        goto __FAILED;
    }

另外裁剪、缩放、旋转等要在创建组以后,使能Chn以前,另外旋转要在裁剪缩放以后

原sensor分辨率则为上面VpssChnarrt设置的分辨率,因为要显示在1280x800的屏幕上,所以要先裁剪原sensor分辨率,使之成为1280x800的倍数

设置裁剪参数时,3456x2160即为裁剪后的分辨率

static RK_S32 create_vpss(VPSS_CFG_S *pstVpssCfg, RK_S32 s32Grp, RK_S32 s32OutChnNum) {
    RK_S32 s32Ret = RK_SUCCESS;
    VPSS_CHN VpssChn[VPSS_MAX_CHN_NUM] = { VPSS_CHN0, VPSS_CHN1, VPSS_CHN2, VPSS_CHN3 };
    VPSS_CROP_INFO_S stCropInfo;

    s32Ret = RK_MPI_VPSS_CreateGrp(s32Grp, &pstVpssCfg->stGrpVpssAttr);
    if (s32Ret != RK_SUCCESS) {
        return s32Ret;
    }
 
    for (RK_S32 i = 0; i < s32OutChnNum; i++) {
        s32Ret = RK_MPI_VPSS_SetChnAttr(s32Grp, VpssChn[i], &pstVpssCfg->stVpssChnAttr[i]);
        if (s32Ret != RK_SUCCESS) {
            return s32Ret;
        }

        stCropInfo.bEnable = RK_TRUE;
        stCropInfo.enCropCoordinate = VPSS_CROP_RATIO_COOR;
        stCropInfo.stCropRect.s32X = 0;    //192 + 3456 + 192 = 3840, when x = 192, video on center
        stCropInfo.stCropRect.s32Y = 0;
        stCropInfo.stCropRect.u32Width = 3456;
        stCropInfo.stCropRect.u32Height = 2160;

        RK_MPI_VPSS_SetChnCrop(s32Grp, VpssChn[i], &stCropInfo);

        s32Ret = RK_MPI_VPSS_SetChnRotation(s32Grp,VpssChn[i], ROTATION_270);
        if (s32Ret != RK_SUCCESS) {
            return s32Ret;
        }      
        
        s32Ret = RK_MPI_VPSS_EnableChn(s32Grp, VpssChn[i]);
        if (s32Ret != RK_SUCCESS) {
            return s32Ret;
        }
    }

    s32Ret = RK_MPI_VPSS_EnableBackupFrame(s32Grp);
    if (s32Ret != RK_SUCCESS) {
        return s32Ret;
    }

    s32Ret = RK_MPI_VPSS_StartGrp(s32Grp);
    if (s32Ret != RK_SUCCESS) {
        return s32Ret;
    }

    return  RK_SUCCESS;
}

旋转则通过s32Ret = RK_MPI_VPSS_SetChnRotation(s32Grp,VpssChn[i], ROTATION_270);

ROTATION_90、180、270

在bind_vpss_vo后,创建一个时间戳线程

    pthread_t tid333;
    // 创建rgn线程
    pthread_create(&tid333, NULL, rgn_overlay_thread, NULL);
void* rgn_overlay_thread(void* arg){
    printf("========%s========\n", __func__);
	RK_S32 s32Ret = RK_SUCCESS;
	RGN_HANDLE RgnHandle = 1;
	BITMAP_S stBitmap;
	RGN_ATTR_S stRgnAttr;
	RGN_CHN_ATTR_S stRgnChnAttr;

	int u32Width = 100;
	int u32Height = 500;
	int s32X = 128;
	int s32Y = 128;

	RK_CHAR *filename = "/data/res/rgn/44";

	MPP_CHN_S stMppChn;

	stMppChn.enModId = RK_ID_VO;
	stMppChn.s32DevId = RK356X_VOP_LAYER_ESMART_0;
	stMppChn.s32ChnId = 0;

	/****************************************
	step 1: create overlay regions
	****************************************/
	stRgnAttr.enType = OVERLAY_RGN;
	stRgnAttr.unAttr.stOverlay.enPixelFmt = (PIXEL_FORMAT_E)RK_FMT_RGBA8888;
	stRgnAttr.unAttr.stOverlay.stSize.u32Width = u32Width;
	stRgnAttr.unAttr.stOverlay.stSize.u32Height = u32Height;
	stRgnAttr.unAttr.stOverlay.u32ClutNum = 0;
    stRgnAttr.unAttr.stOverlay.u32CanvasNum =2;

	s32Ret = RK_MPI_RGN_Create(RgnHandle, &stRgnAttr);
	if (RK_SUCCESS != s32Ret) {
		RK_LOGE("RK_MPI_RGN_Create (%d) failed with %#x!", RgnHandle, s32Ret);
		RK_MPI_RGN_Destroy(RgnHandle);
	}
	RK_LOGI("The handle: %d, create success!", RgnHandle);

	/*********************************************
	step 2: display overlay regions to groups
	*********************************************/
	memset(&stRgnChnAttr, 0, sizeof(stRgnChnAttr));
	stRgnChnAttr.bShow = RK_TRUE;
	stRgnChnAttr.enType = OVERLAY_RGN;
	stRgnChnAttr.unChnAttr.stOverlayChn.stPoint.s32X = s32X;
	stRgnChnAttr.unChnAttr.stOverlayChn.stPoint.s32Y = s32Y;
	stRgnChnAttr.unChnAttr.stOverlayChn.u32BgAlpha = 0;
	stRgnChnAttr.unChnAttr.stOverlayChn.u32FgAlpha = 0;
	stRgnChnAttr.unChnAttr.stOverlayChn.u32Layer = 0;
	stRgnChnAttr.unChnAttr.stOverlayChn.stQpInfo.bEnable = RK_FALSE;
	stRgnChnAttr.unChnAttr.stOverlayChn.stQpInfo.bForceIntra = RK_TRUE;
	stRgnChnAttr.unChnAttr.stOverlayChn.stQpInfo.bAbsQp = RK_FALSE;
	stRgnChnAttr.unChnAttr.stOverlayChn.stQpInfo.s32Qp = RK_FALSE;
	stRgnChnAttr.unChnAttr.stOverlayChn.u32ColorLUT[0] = 0x00;
	stRgnChnAttr.unChnAttr.stOverlayChn.u32ColorLUT[1] = 0xFFFFFF;
	stRgnChnAttr.unChnAttr.stOverlayChn.stInvertColor.bInvColEn = RK_FALSE;
	stRgnChnAttr.unChnAttr.stOverlayChn.stInvertColor.stInvColArea.u32Width = 16;
	stRgnChnAttr.unChnAttr.stOverlayChn.stInvertColor.stInvColArea.u32Height = 16;
	stRgnChnAttr.unChnAttr.stOverlayChn.stInvertColor.enChgMod = LESSTHAN_LUM_THRESH;
	stRgnChnAttr.unChnAttr.stOverlayChn.stInvertColor.u32LumThresh = 100;
	s32Ret = RK_MPI_RGN_AttachToChn(RgnHandle, &stMppChn, &stRgnChnAttr);
	if (RK_SUCCESS != s32Ret) {
		RK_LOGE("RK_MPI_RGN_AttachToChn (%d) failed with %#x!", RgnHandle, s32Ret);
	}
	RK_LOGI("Display region to chn success!");

	/*********************************************
	step 3: show bitmap
	*********************************************/
	RK_S64 s64ShowBmpStart = TEST_COMM_GetNowUs();
	stBitmap.enPixelFormat = (PIXEL_FORMAT_E)RK_FMT_RGBA8888;
	stBitmap.u32Width = u32Width;
	stBitmap.u32Height = u32Height;

	RK_U16 ColorBlockSize = stBitmap.u32Height * stBitmap.u32Width;
	stBitmap.pData = malloc(ColorBlockSize * TEST_ARGB32_PIX_SIZE);

	RK_U8 *ColorData = (RK_U8 *)stBitmap.pData;

    
	//if (filename) {
		//s32Ret = load_file_osdmem(filename, stBitmap.pData, u32Width, u32Height,
		//                          TEST_ARGB32_PIX_SIZE, 0);
        RK_U32 mem_len = u32Width;
        RK_U32 read_len = mem_len * 4 >> 0;
        RK_U32 read_height;
        FILE *file = NULL;

    while(1){
        file = fopen(filename, "rb");
        if (file == NULL) {
            RK_LOGE("open filename: %s file failed!", filename);
            
        }
        for (read_height = 0; read_height < u32Height; read_height++) {
            fread((stBitmap.pData + (u32Width * read_height * 4 >> 0)), 1,
                read_len, file);
        }
        fclose(file);

    
		if (RK_SUCCESS != s32Ret) {
			set_argb8888_buffer((RK_U32 *)ColorData, ColorBlockSize / 4, TEST_ARGB32_RED);
			set_argb8888_buffer((RK_U32 *)(ColorData + ColorBlockSize),
			                    ColorBlockSize / 4, TEST_ARGB32_GREEN);
			set_argb8888_buffer((RK_U32 *)(ColorData + 2 * ColorBlockSize),
			                    ColorBlockSize / 4, TEST_ARGB32_BLUE);
			set_argb8888_buffer((RK_U32 *)(ColorData + 3 * ColorBlockSize),
			                    ColorBlockSize / 4, 0x000000FF);
		}
	//}

    
	s32Ret = RK_MPI_RGN_SetBitMap(RgnHandle, &stBitmap);
	if (s32Ret != RK_SUCCESS) {
		RK_LOGE("RK_MPI_RGN_SetBitMap failed with %#x!", s32Ret);
	}
	RK_S64 s64ShowBmpEnd = TEST_COMM_GetNowUs();
	RK_LOGI("Handle:%d, space time %lld us, load bmp success!", RgnHandle,
	        s64ShowBmpEnd - s64ShowBmpStart);
    sleep(1);
    }
//另一种刷osd的方式
#if 0
    
	/*********************************************
	step 4: use update canvas interface
	*********************************************/
	s64ShowBmpStart = TEST_COMM_GetNowUs();

	RGN_CANVAS_INFO_S stCanvasInfo;
	memset(&stCanvasInfo, 0, sizeof(RGN_CANVAS_INFO_S));

	s32Ret = RK_MPI_RGN_GetCanvasInfo(RgnHandle, &stCanvasInfo);
	if (s32Ret != RK_SUCCESS) {
		RK_LOGE("RK_MPI_RGN_GetCanvasInfo failed with %#x!", s32Ret);
		//return RK_FAILURE;
	}
	memset(reinterpret_cast<void *>(stCanvasInfo.u64VirAddr), 0xff,
					stCanvasInfo.u32VirWidth * stCanvasInfo.u32VirHeight >> 2);
	s32Ret = RK_MPI_RGN_UpdateCanvas(RgnHandle);
	if (s32Ret != RK_SUCCESS) {
		RK_LOGE("RK_MPI_RGN_UpdateCanvas failed with %#x!", s32Ret);
		//return RK_FAILURE;
	}
	s64ShowBmpEnd = TEST_COMM_GetNowUs();
	RK_LOGI("Handle:%d, space time %lld us, update canvas success!", RgnHandle, s64ShowBmpEnd - s64ShowBmpStart);
#endif
    
    
    free(stBitmap.pData);
	return 0;

}

此处的时间戳主要通过读取一个argb格式的文件实现的

通过打开这个argb格式文件,读取到stBitmap,随后通过RK_MPI_RGN_SetBitMap将数据送到画布中

另外通过一个本地运行的python_demo,获取本地时间戳生成argb格式的数据

from PIL import Image, ImageDraw, ImageFont
import os
import datetime
import time

# 获取DejaVuSans字体文件路径(如果不存在,您需要手动指定一个合适的字体文件路径)
font_path = "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
if not os.path.isfile(font_path):
    raise FileNotFoundError("DejaVuSans.ttf font file not found. Please provide a valid font file path.")

# 创建一个空白的RGBA模式图片,大小为500x100像素,背景透明
width, height = 500, 100
font_size = 40
font = ImageFont.truetype(font_path, font_size)

while True:
    # 创建一个空白的RGBA模式图片,大小为500x100像素,背景透明
    img = Image.new("RGBA", (width, height), (0, 0, 0, 0))

    # 在图片上绘制时间
    draw = ImageDraw.Draw(img)

    # 获取当前系统时间
    now = datetime.datetime.now()
    hour = now.hour
    minute = now.minute
    second = now.second

    # 将时间转换为字符串
    time_str = "{:02d}:{:02d}:{:02d}".format(hour, minute, second)

    # 获取文本边界框(bounding box)
    text_bbox = draw.textbbox((0, 0), time_str, font=font)

    # 计算文本尺寸
    text_width = text_bbox[2] - text_bbox[0]
    text_height = text_bbox[3] - text_bbox[1]

    # 计算文本位置以居中显示
    x = (width - text_width) / 2
    y = (height - text_height) / 2

    # 绘制时间文本
    draw.text((x, y), time_str, font=font, fill=(0, 0, 0, 255))  # 文本颜色为黑色

    # 转换为BGRA8888格式
    img_bgra = img.convert("RGBA").tobytes("raw", "BGRA")

    # 将图片旋转90度
    rotated_img = img.rotate(270, expand=True)

    # 保存旋转后的图片
    with open("/data/res/rgn/44", "wb") as f:
        f.write(rotated_img.tobytes())

    # 暂停一秒钟,避免频繁生成图片
    time.sleep(1)

此处要注意大小端问题,虽说是argb格式,要保存为bgra格式,rk3568才能正常读取为argb8888

实际测试这种实现方法应该是有误差的,因为两个程序的生成argb格式图片与获取argb格式图片并不同步

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值