实验(一):彩色空间转换实验 RGB—YUV

实验目标:将所给的RGB空间的 down.rgb 转换为YUV色彩空间的 down.yuv,再将其重新转换为RGB空间,分析结果

一.rgb与yuv格式文件的数据

要对文件中的数据进行处理,首先当然要了解这种文件格式的数据特点。

说是rgb,yuv文件,其实本质上都是没有文件头的raw文件,文件头开始读就直接是数据。而rgb与yuv的数据存储也有一定的规范,即如下图所示:

在这里插入图片描述 在这里插入图片描述

其中YUV的取样格式为4:2:0,即Y全取样的同时,U,V的水平,垂直取样频率都为其二分之一,且存储方式为先存储所有像素的Y,然后是U,最后是V。

RGB的存储方式则是一个一个像素的B,G,R值顺序存储。(B1G1R1B2G2R2…)

二.大体思路与注意事项

思路其实很简单明确:

  1. 读取down.rgb中的数据储存到相应的buffer中。
  2. 将数据经过公式变换后按顺序输入到YUV文件中。
  3. 将得到的yuv数据通过公式变换重新转成RGB输入到新的rgb文件中。

但其中有许多需要留意的注意事项:

1. RGB-YUV的公式选择

如果你上网查找,可以发现许多不同版本的RB-YUV公式,究其原因是有些公式的不同涉及到了是否量化归一化,是否减少计算量等问题。

此外,本次实验设计的是8比特(256级)量化的色彩,对色差信号引入了压缩系数,并进行归一化,最后得到的转化公式为:
Y = 0.299 R + 0.587 G + 0.114 B Y = 0.299R + 0.587G + 0.114B Y=0.299R+0.587G+0.114B

U = − 0.1684 R − 0.3316 G + 0.5 B + 128 U = -0.1684R - 0.3316G + 0.5B + 128 U=0.1684R0.3316G+0.5B+128

V = 0.5 R − 0.4187 G − 0.0831 B + 128 V = 0.5R - 0.4187G - 0.0831B + 128 V=0.5R0.4187G0.0831B+128

R = Y + 1.402 ∗ ( V − 128 ) R = Y + 1.402 * (V - 128) R=Y+1.402(V128)

G = Y − 0.34414 ∗ ( U − 128 ) − 0.71414 ∗ ( V − 128 ) G = Y - 0.34414 * (U - 128) - 0.71414 * (V - 128) G=Y0.34414(U128)0.71414(V128)

B = Y + 1.772 ( U − 128 ) B = Y + 1.772 (U - 128) B=Y+1.772(U128)

2.量化后的码电平分配

亮度信号:256级(0-255)量化后为防止过载,上端留20级,下端留16级,即范围为16 —235

色差信号:上端留15级,下端留16级,即范围为16 —240

3.还原RGB后防止溢出

可以想到很有可能YUV转换为RGB时数值范围会超出0-255,导致溢出失真,所以有必要在程序中加入放溢出的语句。(虽然最后发现我添加的语句并没有什么用。。。)

三,具体代码(C++, Visual Studio)

header.h
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<malloc.h>
void rgb_to_yuv(unsigned char* rgbBuf, unsigned char* yBuf, unsigned char* uBuf, unsigned char* vBuf, int Width, int Height);
void yuv_to_rgb(unsigned char* rBuf, unsigned char* gBuf, unsigned char* bBuf, unsigned char* yBuf, unsigned char* uBuf, unsigned char* vBuf, int Width, int Height);
rgb2yuv.cpp
#include"header.h"
using namespace std;

void rgb_to_yuv(unsigned char* rgbBuf, unsigned char* yBuf, unsigned char* uBuf, unsigned char* vBuf, int Width, int Height)
{
	int y = 0;
	for (int i = 0; i < Width * Height * 3; i = i + 3)
	{
		yBuf[y] = 0.114 * rgbBuf[i] + 0.587 * rgbBuf[i+1] + 0.299 * rgbBuf[i+2];
		//Y范围:16-235
		if (yBuf[y] > 235)
			yBuf[y] = 235;
		if (yBuf[y] < 16)
			yBuf[y] = 16;
		y++;
	}
	int u = 0, v = 0;
	for (int i = 0; i < Width * Height * 3;)
	{
		uBuf[u] = 0.5 * (rgbBuf[i] + rgbBuf[i + 3] + rgbBuf[i + Width * 3] + rgbBuf[i + Width * 3 + 3])/4
			- 0.3316 * (rgbBuf[i + 1] + rgbBuf[i + 4] + rgbBuf[i + Width * 3 + 1] + rgbBuf[i + Width * 3 + 4])/4
			- 0.1684 * (rgbBuf[i + 2] + rgbBuf[i + 5] + rgbBuf[i + Width * 3 + 2] + rgbBuf[i + Width * 3 + 5])/4 + 128;
		vBuf[v] = -0.083 * (rgbBuf[i] + rgbBuf[i + 3] + rgbBuf[i + Width * 3] + rgbBuf[i + Width * 3 + 3])/4
			- 0.4187 * (rgbBuf[i + 1] + rgbBuf[i + 4] + rgbBuf[i + Width * 3 + 1] + rgbBuf[i + Width * 3 + 4])/4
			+ 0.5 * (rgbBuf[i + 2] + rgbBuf[i + 5] + rgbBuf[i + Width * 3 + 2] + rgbBuf[i + Width * 3 + 5])/4 + 128;
		u++;
		v++;
		//U,V范围:16-240
		if (uBuf[u] > 240)
			uBuf[u] = 240;
		if (uBuf[u] < 16)
			uBuf[u] = 16;
        if (vBuf[v] > 240)
			vBuf[v] = 240;
		if (vBuf[v] < 16)
			vBuf[v] = 16;
		if ((i / 3) % 256 == 254)
			i = i + 258 * 3;
		else
			i = i + 6;
	}
}
yuv2rgb.cpp
#include"header.h"
using namespace std;

void yuv_to_rgb(unsigned char* rBuf, unsigned char* gBuf, unsigned char* bBuf, unsigned char* yBuf, unsigned char* uBuf, unsigned char* vBuf, int Width, int Height)
{
	int j = 0;
	for (int i = 0; i < Width * Height;)
	{
		rBuf[i] = yBuf[i] + 1.402 * (vBuf[j] - 128);
		rBuf[i + 1] = yBuf[i + 1] + 1.402 * (vBuf[j] - 128);
		rBuf[i + Width] = yBuf[i + Width] + 1.402 * (vBuf[j] - 128);
		rBuf[i + Width + 1] = yBuf[i + Width + 1] + 1.402 * (vBuf[j] - 128);

		gBuf[i] = yBuf[i] - 0.34414 * (uBuf[j] - 128) - 0.71414 * (vBuf[j] - 128);
		gBuf[i + 1] = yBuf[i + 1] - 0.34414 * (uBuf[j] - 128) - 0.71414 * (vBuf[j] - 128);
		gBuf[i + Width] = yBuf[i + Width] - 0.34414 * (uBuf[j] - 128) - 0.71414 * (vBuf[j] - 128);
		gBuf[i + Width + 1] = yBuf[i + Width + 1] - 0.34414 * (uBuf[j] - 128) - 0.71414 * (vBuf[j] - 128);

		bBuf[i] = yBuf[i] + 1.772 * (uBuf[j] - 128);
		bBuf[i + 1] = yBuf[i + 1] + 1.772 * (uBuf[j] - 128);
		bBuf[i + Width] = yBuf[i + Width] + 1.772 * (uBuf[j] - 128);
		bBuf[i + Width + 1] = yBuf[i + Width + 1] + 1.772 * (uBuf[j] - 128);

		//防RGB溢出
		if (rBuf[i] < 0)
			rBuf[i] = 0;
		if (rBuf[i] > 255)
			rBuf[i] = 255;
		if (gBuf[i] < 0)
			gBuf[i] = 0;
		if (gBuf[i] > 255)
			gBuf[i] = 255;
		if (bBuf[i] < 0)
			bBuf[i] = 0;
		if (bBuf[i] > 255)
			bBuf[i] = 255;
		if (i % 256 == 254)
			i = i + 256 + 2;
		else
			i = i + 2;
		j++;
	}
}
main.cpp
#include"header.h"
using namespace std;

int main(int argc[], char* argv[])
{
    int Width = 256, Height = 256;
    unsigned char *rgbBuf, *yBuf, *uBuf, *vBuf;
    rgbBuf = (unsigned char *)malloc(sizeof (char) * (Width * Height * 3));
    yBuf = (unsigned char *)malloc(sizeof (char) * (Width * Height));
	uBuf = (unsigned char *)malloc(sizeof (char) * (Width * Height / 4));
	vBuf = (unsigned char *)malloc(sizeof (char) * (Width * Height / 4));
    FILE *rgb = NULL;
    FILE *yuv = NULL;
	rgb = fopen(argv[1], "rb");//argv[1]为down.rgb文件位置
    if (rgb == NULL)
	{
        printf("errors in opening rgb file.");
		exit(0);
	}
    fread(rgbBuf, sizeof(unsigned char), Width * Height * 3, rgb);//读出数据
	fclose(rgb);
	rgb_to_yuv(rgbBuf, yBuf, uBuf, vBuf, Width, Height);//RGBtoYUV
	delete []rgbBuf;

	//写入down.yuv
	yuv = fopen(argv[2], "wb");//argv[2]为down.yuv文件位置
	if (yuv == NULL)
	{
        printf("errors in opening yuv file.");
		exit(0);
	}
	fwrite(yBuf, sizeof(unsigned char), Width * Height, yuv);
	fwrite(uBuf, sizeof(unsigned char), Width * Height / 4, yuv);
	fwrite(vBuf, sizeof(unsigned char), Width * Height / 4, yuv);
	fclose(yuv);

	unsigned char *rBuf, *gBuf, *bBuf;
	rBuf = (unsigned char *)malloc(sizeof (unsigned char) * (Width * Height));
	gBuf = (unsigned char *)malloc(sizeof (unsigned char) * (Width * Height));
	bBuf = (unsigned char *)malloc(sizeof (unsigned char) * (Width * Height));
	yuv_to_rgb(rBuf, gBuf, bBuf, yBuf, uBuf, vBuf, Width, Height);//YUVtoRGB

	//写入recover_down.rgb
	FILE *recover_rgb = NULL;
	recover_rgb = fopen(argv[3], "wb");//argv[3]为recover_down.rgb文件位置
	if (rgb == NULL)
	{
        printf("errors in opening recover_rgb file.");
		exit(0);
	}
	for (int i = 0; i < Width * Height; i++)
	{
		fwrite(&bBuf[i], sizeof(unsigned char), 1, rgb);
		fwrite(&gBuf[i], sizeof(unsigned char), 1, rgb);
		fwrite(&rBuf[i], sizeof(unsigned char), 1, rgb);
	}
	delete []bBuf;
	delete []gBuf;
	delete []rBuf;
	fclose(recover_rgb);
    return 0;
}

四,结果与分析

原始图像(down.rgb):img

本实验使用YUVviewerPlus观察图像:得到的 down.yuv 结果为:

在这里插入图片描述

还原为rgb后的图像recover_down.rgb为:(图像反转是因为读取格式为bmp24,默认数据读取顺序颠倒了)
在这里插入图片描述

我们发现有明显的红点和蓝点,出现了数据溢出,添加的防溢出语句并没有起到应有的效果。
原因似乎是数据变量的问题,有进展将第一时间更新文章。。

通过实验有如下总结:

  1. 色彩空间转化的首要任务时理解公式的含义,灵活运用不同的转化公式
  2. 要考虑到码电平分配造成的影响
  3. 需要考虑到数据溢出的可能性
  4. 对raw数据进行处理要了解其数据存储格式
  5. 熟练运用头文件和多个cpp文件的配合,是程序尽可能简洁美观易阅读和修改
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值