大家好,又见面了,我是你们的朋友全栈君。
概述
图像叠加与音频叠加(混音)不同,人耳可以同时听到两种声音,因此混音时需要将两种信号都保留;但视频不同,图像一旦叠加,那么叠加区域人眼就只能看到最上层的图像。本文仅阐述对原始图像数据做修改的叠加方式,不涉及多个plane实现显示级别的图像叠加的知识。因此本文所述的图像叠加基本原理就是:顶层图像的像素直接替换掉底层图像的像素,从而新的图像会显示出叠加效果。
本文针对NV12像素格式图像的叠加(两张图片均为NV12格式),如果想扩展叠加其他图片,则需先将其他图片转为NV12,然后再进行叠加。如下提供bmp格式的顶图转换路径:
顶图(bmp) —> RGB24 —> NV12 —> 执行叠加操作
NV12格式阐述
nv12像素的存储格式可参考:https://blog.csdn.net/lyy901135/article/details/97934892 如下红框地方则为需要替换的区域,只要将如下两个红框内的值替换为对应“顶图”的值就完成了图像的叠加。那么首先就要确认两个红框内的每个像素的位置和红框的大小。
Y数据内的红框
顶图的宽高为144×48,根据NV12的像素组织特点可知,(x,y)对应的红框大小也为144×48,此处的(x,y)对应代码中的(pos_x, pos_y)
。代码中rect_y
表示Y数据中矩形框起始地址,
rect_y = 底图基址(对应坐标原点) w*y x,对应代码rect_y = param->bm_buff param->pos_y * param->bm_w param->pos_x;
矩形框内每个像素点的位置关系:
/* i 遍历行,j遍历列, w=1920, h=1080*/
for (i=0; i < 48; i ) {
/* i 行对应的基址 */
rect_tmp_y = rect_y i*w;
for (j=0; j < 144; j ) {
Y(x j, y i) = rect_tmp_y j;
}
}
UV数据内的红框
顶图的宽高为144×48,根据NV12的像素组织特点可知,(x’,y’)对应的红框大小则为144×24(每两行Y对应一行UV,所以高度减半:48 -> 24)。代码中rect_u
表示UV数据中矩形框起始地址,
rect_u = 底图基址(对应坐标原点) wh yw/2 x,对应代码rect_u = param->bm_buff param->bm_w * param->bm_h param->pos_y * param->bm_w / 2 param->pos_x;
正是由于每两行Y才对应一行UV数据的特性,因此再计算偏移时累加的是w的一半,对应上述公式中 y*w/2
。
矩形框内每个像素点的位置关系:
/* i 遍历行,j遍历列, w=1920, h=1080*/
for (i=0; i < 48; i ) {
/* i 行对应的uv基址,每两行才更新一次uv基址 */
if (i % 2 == 0) {
rect_tmp_u = rect_u i * param->bm_w / 2;
rect_tmp_v = rect_tmp_u 1;
}
/* 每两行Y对应一行UV,Y的偶数行对应UV行的U数据,并且U还要求在偶数列位置才取值; * Y的奇数行对应UV行的V数据,并且V还要求在奇数列才取值。 */
for (j=0; j < 144; j ) {
if ((i % 2 == 0) && (j % 2 == 0)) {
/* U(x j, y i)的位置, 偶数行,偶数列 */
rect_tmp_u = 2;
} else if ((i % 2 == 1) && (j % 2 == 1)) {
/* V(x j, y i)的位置, 奇数行,奇数列 */
rect_tmp_v = 2;
}
}
}
代码
代码语言:javascript复制/* * author: francis.fan@rock-chip.com * date: 2019-8-2 */
typedef struct _overlayParam {
unsigned char *bm_buff; //base map buff
int bm_w; //base map width
int bm_h; //base map height
unsigned char *top_buff; //top overlay buff
int top_w;
int top_h;
int pos_x; //position x [0, bm_w-top_w]
int pos_y;
}overlayParam;
void dump_overlayparam(overlayParam *param)
{
printf("=== overlayParam ===n");
printf("tbm_buff:%pn", param->bm_buff);
printf("tbm_w:%dn", param->bm_w);
printf("tbm_h:%dnn", param->bm_h);
printf("ttop_buff:%pn", param->top_buff);
printf("ttop_w:%dn", param->top_w);
printf("ttop_h:%dnn", param->top_h);
printf("tpos_x:%dn", param->pos_x);
printf("tpos_y:%dn", param->pos_y);
}
/* 功能:nv12图像叠加 */
int pixel_format_nv12_overlay(overlayParam *param)
{
unsigned char *rect_y = NULL;
unsigned char *rect_u = NULL;
unsigned char *rect_v = NULL;
unsigned char *rect_tmp_y = NULL;
unsigned char *rect_tmp_u = NULL;
unsigned char *rect_tmp_v = NULL;
unsigned char *top_y = NULL;
unsigned char *top_u = NULL;
unsigned char *top_v = NULL;
int i, j;
dump_overlayparam(param);
if (!param || !(param->bm_buff) || !(param->top_buff) ||
((param->bm_w * param->bm_h) <= 0) || ((param->top_w * param->top_h) <= 0) ||
(param->pos_x < 0) || (param->pos_y < 0)) {
printf("ERROR: %s input args invalid!n", __func__);
return -EINVAL;
}
/* Position align */
param->pos_x = (param->pos_x / 2) * 2;
param->pos_y = (param->pos_y / 2) * 2;
if (((param->pos_x param->top_w) >= param->bm_w) ||
(((param->pos_y param->top_h) >= param->bm_h))) {
printf("ERROR: %s overlay img size invalid!n", __func__);
return -EINVAL;
}
/* * 以(pos_x, pos_y)为起始点分别向右(x轴)和向下(y轴)画一个矩形框(图片左上角为原点), * 矩形框的款高就是顶图的宽高。该矩形框便是需要替换成顶图的区域。 */
rect_y = param->bm_buff param->pos_y * param->bm_w param->pos_x;
rect_u = param->bm_buff param->bm_w * param->bm_h param->pos_y * param->bm_w / 2 param->pos_x;
rect_v = rect_u 1;
top_y = param->top_buff;
top_u = param->top_buff param->top_w * param->top_h;
top_v = top_u 1;
for (i = 0; i < param->top_h; i ) {
rect_tmp_y = rect_y i * param->bm_w;
if (i % 2 == 0) {
rect_tmp_u = rect_u i * param->bm_w / 2;
rect_tmp_v = rect_tmp_u 1;
}
for (j = 0; j < param->top_w; j ) {
/* Replace y value */
*(rect_tmp_y j) = *(top_y i * param->top_w j);
if ((i % 2 == 0) && (j % 2 == 0)) {
*rect_tmp_u = *top_u;
top_u = 2;
rect_tmp_u = 2;
} else if ((i % 2 == 1) && (j % 2 == 1)) {
*rect_tmp_v = *top_v;
top_v = 2;
rect_tmp_v = 2;
}
}
}
return 0;
}
工程
工程地址(包含Makefile和测试使用的NV12图片): https://download.csdn.net/download/lyy901135/11467723
运行命令:./nv12_add_nv12 images/base_map.nv12 images/top.nv12 images/output.nv12
查看顶图命令:ffplay -s 144x48 -f rawvideo -pixel_format nv12 images/top.nv12
查看叠加后底图命令:ffplay -s 1920x1080 -f rawvideo -pixel_format nv12 images/output.nv12
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。