YUV图像拼接

概述

最近的一个测试项目需要软件实现YUV420sp图像的水平和垂直拼接。程序对时间复杂度和空间复杂度没什么要求,就自己编写代码实现一下,记录一下思路。

YUV420sp

YUV是一种原始图像格式,没有经过编码,比较方便软件处理。本文处理的图像格式为YUV420sp,图像数据中的亮度Y和色度UV分离,并在内存中连续存储。比如一张 4 × 2 图像,以YUV420sp的格式存储就是:Y1Y2Y3Y4Y5Y6Y7Y8U1V1U2V2,
所占的内存空间为:width * height * 3 / 2
在这里插入图片描述
了解了YUV420sp图像的存储格式,实现图像的拼接就变得比较简单,无非就是内存操作。

垂直拼接

首先是垂直拼接。根据YUV图像的存储格式,垂直拼接只需要将两幅图像的Y拼接在一起,UV拼接在一起,再把UV拼接在Y的后面即可。用图片描述如下:
在这里插入图片描述
上图:uY1 uY2 uY3 uY4 uY5 uY6 uY7 uY8 uU1 uV1 uU2 uV2
下图:dY1 dY2 dY3 dY4 dY5 dY6 dY7 dY8 dU1 dV1 dU2 dV2
拼接:uY1 uY2 uY3 uY4 uY5 uY6 uY7 uY8 dY1 dY2 dY3 dY4 dY5 dY6 dY7 dY8 uU1 uV1 uU2 uV2 dU1 dV1 dU2 dV2

用代码实现就是

int VerticalSplicing(FILE *image1, FILE *image2, FILE *result)
{	
	unsigned char *imgY1, *imgY2;

	// malloc memory
	imgY1 = (unsigned char *)malloc(imgWidth * imgHeight * 3 / 2);
	if(NULL == imgY1)
	{
		printf("Malloc memory for %s faild.\n", VNAME(imgY1));
		return -1;
	}
	imgY2 = (unsigned char *)malloc(imgWidth * imgHeight * 3 / 2);
	if(NULL == imgY1)
	{
		printf("Malloc memory for %s faild.\n", VNAME(imgY2));
		return -1;
	}
	
	// Read data from image
	if((imgWidth * imgHeight * 3 / 2) != fread(imgY1, 1, imgWidth * imgHeight * 3 / 2, image1))
	{
		printf("Read image1 faild!\n");
		return -1;
	}
	if((imgWidth * imgHeight * 3 / 2) != fread(imgY2, 1, imgWidth * imgHeight * 3 / 2, image2))
	{
		printf("Read image2 faild!\n");
		return -1;
	}

	// write YUV
	fwrite(imgY1, 1, imgWidth * imgHeight, result);
	fwrite(imgY2, 1, imgWidth * imgHeight, result);
	fwrite(imgY1 + imgWidth * imgHeight, 1, imgWidth * imgHeight / 2, result);
	fwrite(imgY1 + imgWidth * imgHeight, 1, imgWidth * imgHeight / 2, result);

	free(imgY1);
	free(imgY2);
	return 0;
}

水平拼接

相较于垂直拼接,水平拼接稍微复杂一些,需要对图像的每一行进行处理。需要将两幅图对应行的Y连接在一起组成新的Y行,对应行的UV连接在一起组成新的UV行,再把新的UV连接到Y后面。用图片描述如下:
在这里插入图片描述
左图:uY1 uY2 uY3 uY4 uY5 uY6 uY7 uY8 uU1 uV1 uU2 uV2
右图:dY1 dY2 dY3 dY4 dY5 dY6 dY7 dY8 dU1 dV1 dU2 dV2
拼接:uY1 uY2 uY3 uY4 dY1 dY2 dY3 dY4 uY5 uY6 uY7 uY8 dY5 dY6 dY7 dY8 uU1 uV1 dU1 dV1 uU2 uV2 dU2 dV2

用代码实现比纵向拼接也稍微复杂一些

int HorizontalSplicing(FILE *image1, FILE *image2, FILE *result)
{	
	int i;
	unsigned char *imgY1, *imgY2;
	unsigned char *temp_y1, *temp_y2, *temp_uv1, *temp_uv2;
	unsigned char *temp_yuv, *out_yuv;

	//malloc memory
	imgY1 = (unsigned char *)malloc(imgWidth * imgHeight * 3 / 2);		//image1
	if(NULL == imgY1)
	{
		printf("Malloc memory for %s faild.\n", VNAME(imgY1));
		return -1;
	}
	imgY2 = (unsigned char *)malloc(imgWidth * imgHeight * 3 / 2);		//image2	
	if(NULL == imgY2)
	{
		printf("Malloc memory for %s faild.\n", VNAME(imgY2));
		return -1;
	}
	out_yuv = (unsigned char *)malloc(imgWidth * imgHeight * 3);			// output
	if(NULL == out_yuv)
	{
		printf("Malloc memory for %s faild.\n", VNAME(out_yuv));
		return -1;
	}
	// Read data from image
	if((imgWidth * imgHeight * 3 / 2) != fread(imgY1, 1, imgWidth * imgHeight * 3 / 2, image1))
	{
		printf("Read image1 faild!\n");
		return -1;
	}
	if((imgWidth * imgHeight * 3 / 2) != fread(imgY2, 1, imgWidth * imgHeight * 3 / 2, image2))
	{
		printf("Read image2 faild!\n");
		return -1;
	}
	temp_y1  = imgY1;
	temp_y2  = imgY2;
	temp_uv1 = imgY1 + imgWidth * imgHeight;		// Y + offset
	temp_uv2 = imgY2 + imgWidth * imgHeight;
	temp_yuv = out_yuv;

	// Copy Y
	for(i = 0; i < imgHeight; i++)
	{	
		memcpy(temp_yuv, temp_y1, imgWidth);
		temp_y1 += imgWidth;
		temp_yuv += imgWidth;
		memcpy(temp_yuv, temp_y2, imgWidth);
		temp_y2 += imgWidth;
		temp_yuv += imgWidth;
	}

	// Copy UV
	for(i = 0; i < imgHeight / 2; i++)
	{
		memcpy(temp_yuv, temp_uv1, imgWidth);
		temp_uv1 += imgWidth;
		temp_yuv += imgWidth;
		memcpy(temp_yuv, temp_uv2, imgWidth);
		temp_uv2 += imgWidth;
		temp_yuv += imgWidth;
	}

	// Write data to file
	fwrite(out_yuv, 1, imgWidth * imgHeight * 3, result);

	free(imgY1);
	free(imgY2);
	free(out_yuv);
	
	return 0;
}

结果

使用了两张640 * 360 的YUV图像进行了测试,可以实现垂直拼接和水平拼接,多次拼接还可以实现4窗格。
在这里插入图片描述
最新整理的程序已经提交到gitee上,地址为:https://gitee.com/mrs1023/image_process

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值