欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

数据压缩·作业四(rgb文件与yuv文件的相互转换)

程序员文章站 2022-03-23 11:02:55
...

一、实验内容

掌握彩色空间转换的基本思想及转换公式
(1)YUV与RGB空间的相互转换由电视原理可知,亮度和色差信号的构成如下:
Y=0.2990R+0.5870G+0.1140B
R-Y=0.7010R-0.5870G-0.1140B
B-Y=-0.2990R-0.5870G+0.8860B
为了使色差信号的动态范围控制在0.5之间,需要进行归一化,对色差信号引入压缩系数。归一化后的色差信号为:
U=-0.1684R-0.3316G+0.5B
V=0.5R-0.4187G-0.0813B

(2) 码电平分配及数字表达式
亮电平信号量化后码电平分配
在对分量信号进行8比特均匀量化时,共分为256个等间隔的量化级。为了防止信号变动造成过载,在256级上端留20级,下端留16级作为信号超越动态范围的保护带。
色差信号量化后码电平分配
色差信号经过归一化处理后,动态范围为-0.5-0.5,让色差零电平对应码电平128, 色差信号总共占225个量化级。在256级上端留15级,下端留16级作为信号超越动态范围 的保护带。
(3)色度格式
4:2:0格式是指色差信号U,V的取样频率为亮度信号取样频率的四分之一,在水平方 向和垂直方向上的取样点数均为Y的一半

二、实验原理

(1)关于彩色空间转换和码电平分配公式的推导

{Y=0.2990R+0.5870G+0.1140BRY=0.7010R0.5870G0.1140BBY=0.2990R0.5870G+0.8860B \begin{cases} Y=0.2990R+0.5870G+0.1140B\\ R-Y=0.7010R-0.5870G-0.1140B\\ B-Y=-0.2990R-0.5870G+0.8860B \end{cases}

归一化后:
{Y=0.2990R+0.5870G+0.1140BU=0.564(BY)=0.1684R0.3316G+0.5BV=0.713(RY)=0.5R0.4187G0.0813B \begin{cases} Y=0.2990R+0.5870G+0.1140B\\ U=0.564(B-Y)=-0.1684R-0.3316G+0.5B\\ V=0.713(R-Y)=0.5R-0.4187G-0.0813B \end{cases}
其中 RGB[0,1]R、G、B\in {[0,1]},则Y[0,1]Y\in {[0,1]},U&V[0.5,0.5]\in {[-0.5,0.5]}.
要求对Y、U、V进行8bit量化,即分28=2562^8=256为级。
对于Y分量,上留20级,下留16级,即量化后Y’[16,235]\in {[16,235]}
Y=(23516)Y+16=219Y+16Y'=(235-16)Y+16=219Y+16.
对于U&V分量,上留15级,下留16级,即量化后U’&V’[16,240]\in {[16,240]}
U=(24016)U+128=224U+128U'=(240-16)U+128=224U+128V=(24016)V+128=224V+128V'=(240-16)V+128=224V+128.
以上公式对R、G、B未经量化,但本次实验要处理的文件的R、G、B已进行了量化,量化区间为[0,255],所以以上的YUV分量相当于不用量化。
综上,RGB转YUV的公式为:
{Y=219Y+16255=66R+129G+25B255U=224U+128255=38R74G+112B255V=224V+128255=112R94G18B255 \begin{cases} Y=\frac{219Y+16}{255}=\frac{66R+129G+25B}{255}\\ U=\frac{224U+128}{255}=\frac{-38R-74G+112B}{255}\\ V=\frac{224V+128}{255}=\frac{112R-94G-18B}{255} \end{cases}
把系数矩阵做反变换即可得到YUV转RGB的公式,为:
{R=298Y+411V57344255G=298Y101U211V+34739255B=298Y+519U71117255 \begin{cases} R=\frac{298Y+411V-57344}{255}\\ G=\frac{298Y-101U-211V+34739}{255}\\ B=\frac{298Y+519U-71117}{255} \end{cases}

(2)RGB转YUV

通过公式可以把YUV分量分别转换出来,但是转出来的Y、U、V分量数据量是一样的,即转换出来的文件是4:4:4色度取样格式,但此实验要求色度取样格式为4:2:0,所以要对U、V分量做下采样
注:4:2:0取样格式即U&V分量是隔行隔列采样的,得到的数据量是Y的四分之一。

(3)YUV转RGB

通过公式可以把RGB转换出来,需要注意的是YUV文件是4:2:0色度取样格式的,需要进行上采样,把其变成4:4:4取样格式才能套进公式里进行转换。

*(4)上采样原理和下采样原理

上采样原理:图像放大几乎都是采用内插值方法,即在原有图像像素的基础上在像素点之间采用合适的插值算法插入新的元素。
下采样原理:对于一幅图像I尺寸为MN,对其进行s倍下采样,即得到(M/s)(N/s)尺寸的得分辨率图像,当然s应该是M和N的公约数才行,如果考虑的是矩阵形式的图像,就是把原始图像s*s窗口内的图像变成一个像素,这个像素点的值就是窗口内所有像素的均值。

三、实验步骤

(1)设置命令参数

数据压缩·作业四(rgb文件与yuv文件的相互转换)

(2)rgb2yuv

1、使用公式进行RGB向YUV的转换

//rgb2yuv444
	for (int i = 0;i<picHeight * picWidth;i++)
	{
		*(y + i) = (int)((*(r + i) * 66 + *(g + i) * 129 + *(b + i) * 25) / 255 + 16);
		*(u + i) = (int)((*(r + i)*(-38) + *(g + i)*(-74) + *(b + i) * 112) / 255 + 128);
		*(v + i) = (int)((*(r + i)*(112) + *(g + i)*(-94) + *(b + i)*(-18)) / 255 + 128);
	}
//end

2、对YUV分量进行下采样得到4:2:0取样格式的YUV分量

//downSample:yuv444 to yuv420
	int k = 0;
	for (int i = 0; i < picHeight / 2;i++)
	{
		for (int j = 0;j < picWidth / 2;j++)
		{
			*(u420 + k) = (*(u + picWidth * i * 2 + 2 * j) + *(u + picWidth * i * 2 + 2 * j + 1) + *(u + picWidth  * (2 * i + 1) + 2 * j) + *(u + picWidth  * (2 * i + 1) + 2 * j + 1)) / 4;
			*(v420 + k) = (*(v + picWidth * i * 2 + 2 * j) + *(v + picWidth * i * 2 + 2 * j + 1) + *(v + picWidth  * (2 * i + 1) + 2 * j) + *(v + picWidth  * (2 * i + 1) + 2 * j + 1)) / 4;
			k++;
			//¥ÌŒÛ£∫*(u444 + picWidth * i  + 2 * j)µ»
		}
	}
//end downSample

3、将YUV分量写入YUV文件中

//write yuvFile
	fwrite(y, 1, picHeight * picWidth, yuv);
	fwrite(u420, 1, picHeight * picWidth / 4, yuv);
	fwrite(v420, 1, picHeight * picWidth / 4, yuv);
//end write

(3)yuv2rgb

1、将YUV分量上采样得到新的YUV分量

//yuv420 to yuv444
	int k = 0;
	for (int i = 0;i < picHeight/2 ;i++)
	{
		for (int j = 0;j < picWidth/2 ;j++)
		{
			*(u444 + picWidth * 2 * i + 2 * j) = *(u + k);
			*(u444 + picWidth * 2 * i + 2 * j + 1) = *(u + k);
			*(u444 + picWidth * (2 * i + 1) + 2 * j) = *(u + k);
			*(u444 + picWidth * (2 * i + 1) + 2 * j + 1) = *(u + k);

			*(v444 + picWidth * 2 * i + 2 * j) = *(v + k);
			*(v444 + picWidth * 2 * i + 2 * j + 1) = *(v + k);
			*(v444 + picWidth * (2 * i + 1) + 2 * j) = *(v + k);
			*(v444 + picWidth * (2 * i + 1) + 2 * j + 1) = *(v + k);
			k++;
		}
	}
//end

2、通过公式进行YUV向RGB的转换并进行修正

//yuv2rgb
	for (int i = 0;i < picHeight*picWidth;i++)
	{
		*(r + i) = int((*(y + i) * 298 + *(v444 + i) * 411 - 57344) / 255);
		*(g + i) = int((*(y + i) * 298 - *(u444 + i) * 101 - *(v444 + i) * 211 + 34739) / 255);
		*(b + i) = int((*(y + i) * 298 + *(u444 + i) * 519 - 71117) / 255);
		if (*(r + i) < 0)
			*(r + i) = 0;
		if (*(r + i) > 255)
			*(r + i) = 255;
		if (*(g + i) < 0)
			*(g + i) = 0;
		if (*(g + i) > 255)
			*(g + i) = 255;
		if (*(b + i) < 0)
			*(b + i) = 0;
		if (*(b + i) > 255)
			*(b + i) = 255;
	}
//end

3、将RGB写入RGB文件中

//write rgbFile (b-g-r)
	unsigned char *rgbBuffer;
	rgbBuffer = (unsigned char *)malloc(picHeight*picWidth * 3);
	for (int i = 0;i < picHeight*picWidth;i++)
	{
		*(rgbBuffer + 3 * i) = *(b + i);
		*(rgbBuffer + 3 * i + 1) = *(g + i);
		*(rgbBuffer + 3 * i + 2) = *(r + i);
	}
	fwrite(rgbBuffer, 1, picHeight*picWidth * 3, rgb);
//end write

四、完整代码

head.h

#pragma once
void rgb2yuv(unsigned long picHeight, unsigned long picWidth, unsigned char *buffer, unsigned char *y, unsigned char *u, unsigned char *v,FILE *yuv);
void yuv2rgb(unsigned long picHeight, unsigned long picWidth, unsigned char *buffer2, int *r, int *g, int *b, FILE *rgb);

rgb2yuv.cpp

#include<stdio.h>
#include<stdlib.h>
#include "head.h"

void rgb2yuv(unsigned long picHeight, unsigned long picWidth, unsigned char *buffer, unsigned char *y,unsigned char *u,unsigned char *v,FILE *yuv)
{

//allocate buffer 
	unsigned char  *r, *g, *b, *u420, *v420;
	r = (unsigned char *)malloc(picHeight * picWidth);
	g = (unsigned char *)malloc(picHeight * picWidth);
	b = (unsigned char *)malloc(picHeight * picWidth);
	u420 = (unsigned char *)malloc(picHeight * picWidth / 4);
	v420 = (unsigned char *)malloc(picHeight * picWidth / 4);
//end allocate


//get r & g & b buffer
	for (int i = 0;i<picHeight * picWidth;i++)
	{
		//rgb¿Ô¥Ê¥¢À≥–ÚŒ™b-g-r
		*(b + i) = *(buffer + 3 * i);
		*(g + i) = *(buffer + 3 * i + 1);
		*(r + i) = *(buffer + 3 * i + 2);
		//¥ÌŒÛ:*(b + i) = *(buffer + i);*(g + i) = *(buffer + i + 1);*(r + i) = *(buffer + i + 2);
	}
//end


//rgb2yuv444
	for (int i = 0;i<picHeight * picWidth;i++)
	{
		*(y + i) = (int)((*(r + i) * 66 + *(g + i) * 129 + *(b + i) * 25) / 255 + 16);
		*(u + i) = (int)((*(r + i)*(-38) + *(g + i)*(-74) + *(b + i) * 112) / 255 + 128);
		*(v + i) = (int)((*(r + i)*(112) + *(g + i)*(-94) + *(b + i)*(-18)) / 255 + 128);
	}
//end


//downSample:yuv444 to yuv420
	int k = 0;
	for (int i = 0; i < picHeight / 2;i++)
	{
		for (int j = 0;j < picWidth / 2;j++)
		{
			*(u420 + k) = (*(u + picWidth * i * 2 + 2 * j) + *(u + picWidth * i * 2 + 2 * j + 1) + *(u + picWidth  * (2 * i + 1) + 2 * j) + *(u + picWidth  * (2 * i + 1) + 2 * j + 1)) / 4;
			*(v420 + k) = (*(v + picWidth * i * 2 + 2 * j) + *(v + picWidth * i * 2 + 2 * j + 1) + *(v + picWidth  * (2 * i + 1) + 2 * j) + *(v + picWidth  * (2 * i + 1) + 2 * j + 1)) / 4;
			k++;
			//¥ÌŒÛ£∫*(u444 + picWidth * i  + 2 * j)µ»
		}
	}
//end downSample


//write yuvFile
	fwrite(y, 1, picHeight * picWidth, yuv);
	fwrite(u420, 1, picHeight * picWidth / 4, yuv);
	fwrite(v420, 1, picHeight * picWidth / 4, yuv);
//end write


//freebuffer
	free(r);
	free(g);
	free(b);
	free(u420);
	free(v420);
//end free

}

yuv2rgb.cpp

#include <stdio.h>
#include <stdlib.h>
#include "head.h"

void yuv2rgb(unsigned long picHeight, unsigned long picWidth, unsigned char *buffer2, int *r, int *g, int *b, FILE *rgb)
{

//allocate buffer
	unsigned char *y, *u, *v,*u444,*v444;
	y = (unsigned char *)malloc(picHeight * picWidth);
	u = (unsigned char *)malloc(picHeight * picWidth /4);
	v = (unsigned char *)malloc(picHeight * picWidth /4);
	u444 = (unsigned char *)malloc(picHeight * picWidth);
	v444 = (unsigned char *)malloc(picHeight * picWidth);
//end allocate

//get y & u & v buffer
	for (int i = 0;i < picHeight*picWidth;i++)
	{
		*(y + i) = *(buffer2 + i);
	}
	for (int i = 0;i < picHeight*picWidth/4;i++)
	{
		*(u + i) = *(buffer2 + i + picHeight*picWidth);
		*(v + i) = *(buffer2 + i + picHeight*picWidth * 5 / 4);
	}
//end

//yuv420 to yuv444
	int k = 0;
	for (int i = 0;i < picHeight/2 ;i++)
	{
		for (int j = 0;j < picWidth/2 ;j++)
		{
			*(u444 + picWidth * 2 * i + 2 * j) = *(u + k);
			*(u444 + picWidth * 2 * i + 2 * j + 1) = *(u + k);
			*(u444 + picWidth * (2 * i + 1) + 2 * j) = *(u + k);
			*(u444 + picWidth * (2 * i + 1) + 2 * j + 1) = *(u + k);

			*(v444 + picWidth * 2 * i + 2 * j) = *(v + k);
			*(v444 + picWidth * 2 * i + 2 * j + 1) = *(v + k);
			*(v444 + picWidth * (2 * i + 1) + 2 * j) = *(v + k);
			*(v444 + picWidth * (2 * i + 1) + 2 * j + 1) = *(v + k);
			k++;
		}
	}
//end


//yuv2rgb
	for (int i = 0;i < picHeight*picWidth;i++)
	{
		*(r + i) = int((*(y + i) * 298 + *(v444 + i) * 411 - 57344) / 255);
		*(g + i) = int((*(y + i) * 298 - *(u444 + i) * 101 - *(v444 + i) * 211 + 34739) / 255);
		*(b + i) = int((*(y + i) * 298 + *(u444 + i) * 519 - 71117) / 255);

		//修正
		if (*(r + i) < 0)
			*(r + i) = 0;
		if (*(r + i) > 255)
			*(r + i) = 255;
		if (*(g + i) < 0)
			*(g + i) = 0;
		if (*(g + i) > 255)
			*(g + i) = 255;
		if (*(b + i) < 0)
			*(b + i) = 0;
		if (*(b + i) > 255)
			*(b + i) = 255;
	}
//end

//write rgbFile (b-g-r)
	unsigned char *rgbBuffer;
	rgbBuffer = (unsigned char *)malloc(picHeight*picWidth * 3);
	for (int i = 0;i < picHeight*picWidth;i++)
	{
		*(rgbBuffer + 3 * i) = *(b + i);
		*(rgbBuffer + 3 * i + 1) = *(g + i);
		*(rgbBuffer + 3 * i + 2) = *(r + i);
	}
	fwrite(rgbBuffer, 1, picHeight*picWidth * 3, rgb);
//end write

//free buffer
	free(y);
	free(u);
	free(v);
	free(u444);
	free(v444);
	free(rgbBuffer);
//end free
}

main.cpp

#include <stdio.h>
#include <stdlib.h>
#include "head.h"

int main(int argc, const char * argv[])
{
	
	FILE *down_rgb = NULL;
	FILE *yuv = NULL;
	FILE *rgb = NULL;

//open rgb & yuv file
	if ((down_rgb = fopen(argv[1], "rb")) == NULL)
	{
		printf("down.rgb file open fail!");
		exit(0);
	}
	if ((yuv = fopen(argv[2], "wb")) == NULL)
	{
		printf("yuv file fail!");
		exit(0);
	}
	if ((rgb = fopen(argv[5], "wb")) == NULL)
	{
		printf("rgb file fail!");
		exit(0);
	}
//end open

//get the height & width of pic
	unsigned long picHeight, picWidth;
	picHeight = atoi(argv[3]);
	picWidth = atoi(argv[4]);
//end



//confirm length of down.rgb
	int buffersize;
	fseek(down_rgb, 0, SEEK_END);
	buffersize = ftell(down_rgb);
	fseek(down_rgb, 0, SEEK_SET);
	printf("down.rgb length:%d\n", buffersize);
//end

//allocate buffer for rgb2yuv
	unsigned char *buffer, *y,*u,*v;
	buffer = (unsigned char *)malloc(buffersize);
	y = (unsigned char *)malloc(picHeight * picWidth);
	u = (unsigned char *)malloc(picHeight * picWidth);
	v = (unsigned char *)malloc(picHeight * picWidth);
//end allocate

//get buffer
	fread(buffer, 1, buffersize, down_rgb);
//end

//rgb2yuv
	rgb2yuv(picHeight,picWidth,buffer,y,u,v,yuv);
//end 



//“ª∂®“™close yuvFile ‘Ÿ¥Úø™
	fclose(yuv);
	if ((yuv = fopen(argv[2], "rb")) == NULL)
	{
		printf("yuv file fail!");
		exit(0);
	}

//confirm length of yuvFile.yuv
	int buffer2size;
	fseek(yuv, 0, SEEK_END);
	buffer2size = ftell(yuv);
	fseek(yuv, 0, SEEK_SET);
	printf("yuvFile length:%d\n", buffer2size);
	//end

//allocate buffer for yuv2rgb
	unsigned char *buffer2;
	int *r, *g, *b;
	buffer2 = (unsigned char *)malloc(buffer2size);
	r = (int *)malloc(sizeof(int)* picHeight*picWidth);
	g = (int *)malloc(sizeof(int)* picHeight*picWidth);
	b = (int *)malloc(sizeof(int)* picHeight*picWidth);
//end allocate

//get buffer2
	fread(buffer2, 1, buffer2size, yuv);
//end

//yuv2rgb
	yuv2rgb(picHeight,picWidth,buffer2,r,g,b,rgb);
//end



//free buffer & close file
	free(buffer);
	free(y);
	free(u);
	free(v);
	free(buffer2);
	free(r);
	free(g);
	free(b);

	fclose(down_rgb);
	fclose(yuv);
	fclose(rgb);
//end

	getchar();

	return 0;
}

五、结果

原图:down.rgb rgb2yuv:yuvFile.yuv yuv2rgb:rgbFile.rgb
数据压缩·作业四(rgb文件与yuv文件的相互转换) 数据压缩·作业四(rgb文件与yuv文件的相互转换) 数据压缩·作业四(rgb文件与yuv文件的相互转换)

由于原图与rgbFile采用bmp显示,所以图像是倒着的(bmp倒着存储数据)。

六、错误图样及分析

(1)进行yuv2rgb实验中,YUV文件应该先close再以只读方式打开,否则错误如下:
数据压缩·作业四(rgb文件与yuv文件的相互转换)
(2)yuv文件打开的方式应是’wb’,而不是’w’,否则如下错误:
数据压缩·作业四(rgb文件与yuv文件的相互转换)
*注:
“r” = “rt”
打开一个文本文件,文件必须存在,只允许读
“r+” = “rt+”
打开一个文本文件,文件必须存在,允许读写
“rb”
打开一个二进制文件,文件必须存在,只允许读
“rb+”
打开一个二进制文件,文件必须存在,允许读写
“w” = “wt”
新建一个文本文件,已存在的文件将内容清空,只允许写

“w+” = “wt+”
新建一个文本文件,已存在的文件将内容清空,允许读写
“wb”
新建一个二进制文件,已存在的文件将内容清空,只允许写

“wb+”
新建一个二进制文件,已存在的文件将内容清空,允许读写
(3)yuv2rgb中,r、g、b没有用int类型而用了unsigned char 类型(取值范围0~255)导致数值溢出,错误如下图:
数据压缩·作业四(rgb文件与yuv文件的相互转换)
(4)yuv2rgb中没有进行修正,导致有些数值不在[0,255]区间,错误如下图:
数据压缩·作业四(rgb文件与yuv文件的相互转换)

七、误差分析

相关标签: 数据压缩