YUV4:2:0格式与RGB格式的图片转化

实验目的

一、课上老师已给出RGB转YUV格式的示例程序,阅读并理解该程序。
二、编写将YUV转换为RGB的程序。将给定的实验数据用该程序转换为RGB文件。并与原RGB文件进行比较,如果有误差,分析误差形成的原因。

算法基本原理

该实验中,我使用的图像值域是[0,255]。值域为[0,255]常用的转换公式如下。

RGB转YUV的公式:

Y = 0.2990R + 0.5870G + 0.1140B

U = - 0.1684R - 0.3316G + 0.5B + 128

V = 0.5R - 0.4187G - 0.0813B + 128

对应YUV转RGB:

R = Y + 1.403(V - 128)

G = Y - 0.343(U - 128)- 0.714(V - 128)

B = Y + 1.770(U - 128)
YUV流的采样方式

YUV码流的存储格式其实与其采样的方式密切相关,主流的采样方式有三种:

  • YUV4:4:4:表示一个Y分量对应一组UV分量。
  • YUV4:2:2:表示两个Y分量共用一组UV分量。
  • YUV4:1:1: 是在水平方向上对色度进行4:1抽样。
  • YUV4:2:0:表示四个Y分量共用一组UV分量。

下面是四种采样方式的示意图在这里插入图片描述

本实验是:YUV4:2:0 采样方式为: U分量和V分量隔行采样, 同时UV分量在其采样行也是隔行采样。
rgb存储方式是
在这里插入图片描述
每个像素对应bgr三个unsigned char。
为考虑算法简便,这里采用的是按照u,v递增顺序取值
先取yuv,再取(y+256)uv,然后y++,再取yuv,再取(y+256)uv,然后y++,u++,v++。这样四个为一组,形成循环。

代码部分

程序功能函数部分:

void YUV2RGB(FILE* yuv,FILE* rgb,int width,int height)
{
    
    
	Init();
	int i = 0, j = 0;
	long shape = width * height;
	unsigned char* y, * u, * v, * rgb1, * auxrgb, * start;
	y = (unsigned char*)malloc(sizeof(unsigned char*) * shape);
	u = (unsigned char*)malloc(sizeof(unsigned char*) * shape / 4);
	v = (unsigned char*)malloc(sizeof(unsigned char*) * shape / 4);
	rgb1 = (unsigned char*)malloc(sizeof(unsigned char*) * shape * 3);//没必要将rgb分开,*3即可
	start = rgb1;//rgb1首指针
	fread(y, shape, sizeof(unsigned char), yuv);
	fread(u, shape / 4, sizeof(unsigned char), yuv);
	fread(v, shape / 4, sizeof(unsigned char), yuv);
	for (j = 0; j < height/2; j++)
	{
    
    
		auxrgb = rgb1 + 3 * width;//偶数行
		for(i=0;i<width/2;i++)
		{
    
    
			*rgb1++ = Rgu(*y + YUVRGB1403[*u]);
			*rgb1++ = Rgu(*y - YUVRGB0343[*u] - YUVRGB0714[*v]);
			*rgb1++ = Rgu(*y + YUVRGB1770[*v]);

			*auxrgb++ = Rgu(*(width + y) + YUVRGB1403[*u]);
			*auxrgb++ = Rgu(*(width + y) - YUVRGB0343[*u] - YUVRGB0714[*v]);
			*auxrgb++ = Rgu(*(width + y++) + YUVRGB1770[*v]);

			*rgb1++ = Rgu(*y + YUVRGB1403[*u]);
			*rgb1++ = Rgu(*y - YUVRGB0343[*u] - YUVRGB0714[*v]);
			*rgb1++ = Rgu(*y + YUVRGB1770[*v]);

			*auxrgb++ = Rgu(*(width + y) + YUVRGB1403[*u]);
			*auxrgb++ = Rgu(*(width + y) - YUVRGB0343[*u++] - YUVRGB0714[*v]);
			*auxrgb++ = Rgu(*(width + y++) + YUVRGB1770[*v++]);
		}
		y += width; rgb1 += 3 * width;
	}

	if (shape * 3 == fwrite(start, sizeof(unsigned char*), shape * 3, rgb)) 
		printf("success");
	else printf("failed to write");
	free(start); 
	free(y - shape);
	free(u - shape / 4);
	free(v - shape / 4);
}

查找表部分

static float YUVRGB1403[256], YUVRGB0343[256], YUVRGB0714[256], YUVRGB1770[256];

void Init()
{
    
    
	int i = 0;
	for (i; i < 256; i++)
	{
    
    
		YUVRGB1403[i] = 1.403 * (i - 128);
		YUVRGB0343[i] = 0.343 * (i - 128);
		YUVRGB0714[i] = 0.714 * (i - 128);
		YUVRGB1770[i] = 1.770 * (i - 128);
	}
}

main函数

int main(int argc,char **argv)
{
    
    

	if (argc == 5)
	{
    
    
		FILE* yuv = fopen(argv[1], "rb");
		FILE* rgb = fopen(argv[2], "wb");
		YUV2RGB(yuv, rgb, atoi(argv[3]), atoi(argv[4]));
		fclose(yuv);
		fclose(rgb);
	}
	else printf("需要4个参数");
	return 0;
}

这里直接将文件路径和图片大小均传递给*argv[],省去了判断环节,代码更简洁,代价是少了严谨性。
完整代码

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
static float YUVRGB1403[256], YUVRGB0343[256], YUVRGB0714[256], YUVRGB1770[256];

int main(int argc,char **argv)
{
    
    

	if (argc == 5)
	{
    
    
		FILE* yuv = fopen(argv[1], "rb");
		FILE* rgb = fopen(argv[2], "wb");
		YUV2RGB(yuv, rgb, atoi(argv[3]), atoi(argv[4]));
		fclose(yuv);
		fclose(rgb);
	}
	else printf("需要4个参数");
	return 0;
}
unsigned char Rgu(float a)
{
    
    
	if (a < 0)	return (unsigned char)0;
	else if (a > 255) return (unsigned char)255;
	else return (unsigned char)a;
}

void YUV2RGB(FILE* yuv,FILE* rgb,int width,int height)
{
    
    
	Init();
	int i = 0, j = 0;
	long shape = width * height;
	unsigned char* y, * u, * v, * rgb1, * auxrgb, * start;
	y = (unsigned char*)malloc(sizeof(unsigned char*) * shape);
	u = (unsigned char*)malloc(sizeof(unsigned char*) * shape / 4);
	v = (unsigned char*)malloc(sizeof(unsigned char*) * shape / 4);
	rgb1 = (unsigned char*)malloc(sizeof(unsigned char*) * shape * 3);//没必要将rgb分开,*3即可
	start = rgb1;//rgb1首指针
	fread(y, shape, sizeof(unsigned char), yuv);
	fread(u, shape / 4, sizeof(unsigned char), yuv);
	fread(v, shape / 4, sizeof(unsigned char), yuv);
	for (j = 0; j < height/2; j++)
	{
    
    
		auxrgb = rgb1 + 3 * width;//偶数行
		for(i=0;i<width/2;i++)
		{
    
    
			*rgb1++ = Rgu(*y + YUVRGB1403[*u]);
			*rgb1++ = Rgu(*y - YUVRGB0343[*u] - YUVRGB0714[*v]);
			*rgb1++ = Rgu(*y + YUVRGB1770[*v]);

			*auxrgb++ = Rgu(*(width + y) + YUVRGB1403[*u]);
			*auxrgb++ = Rgu(*(width + y) - YUVRGB0343[*u] - YUVRGB0714[*v]);
			*auxrgb++ = Rgu(*(width + y++) + YUVRGB1770[*v]);

			*rgb1++ = Rgu(*y + YUVRGB1403[*u]);
			*rgb1++ = Rgu(*y - YUVRGB0343[*u] - YUVRGB0714[*v]);
			*rgb1++ = Rgu(*y + YUVRGB1770[*v]);

			*auxrgb++ = Rgu(*(width + y) + YUVRGB1403[*u]);
			*auxrgb++ = Rgu(*(width + y) - YUVRGB0343[*u++] - YUVRGB0714[*v]);
			*auxrgb++ = Rgu(*(width + y++) + YUVRGB1770[*v++]);
		}
		y += width; rgb1 += 3 * width;
	}

	if (shape * 3 == fwrite(start, sizeof(unsigned char*), shape * 3, rgb)) 
		printf("success");
	else printf("failed to write");
	free(start); 
	free(y - shape);
	free(u - shape / 4);
	free(v - shape / 4);
}
void Init()
{
    
    
	int i = 0;
	for (i; i < 256; i++)
	{
    
    
		YUVRGB1403[i] = 1.403 * (i - 128);
		YUVRGB0343[i] = 0.343 * (i - 128);
		YUVRGB0714[i] = 0.714 * (i - 128);
		YUVRGB1770[i] = 1.770 * (i - 128);
	}
}

实验结果

rgb原图在这里插入图片描述转换后的rgb图在这里插入图片描述

实验结论

经过肉眼对比发现色彩稍有偏差
这是因为yuv4:2:0格式造成的图像色度的采样率低了,色度信号的层次有损失,色彩的过渡不够“圆润”。

猜你喜欢

转载自blog.csdn.net/weixin_44218153/article/details/114947643
今日推荐