数据压缩学习实验(一)RGB与YUV色彩空间转换的C++实现及误差分析

实验目的

  1. 编写程序将RGB图片文件转换为YUV色彩空间。其中,RGB文件各像素以BGR顺序按行依次排列存储,YUV文件按全部Y数据、全部U数据、全部V数据存储,均为8bit量化。
  2. 编写程序将图片从YUV色彩空间转换回RGB色彩空间,并于原RGB文件进行比较,分析误差。

实验原理

  1. RGB与YUV空间的相互转换:
    由电视原理可知,亮度和色差信号的构成如下:
    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:4:4:表示RGB取样,RGB信号的取样点数量一样。
    • 4:2:2:色差信号U和V在水平方向取样点数为Y的二分之一,而垂直方向取样点数与Y相同。
    • 4:2:0:色差信号U和V在水平和垂直方向的取样点数均为Y的二分之一。
    • 4:1:1:色差信号Cr和Cb在水平方向取样点数为Y的四分之一,而 垂直方向取样点数与Y相同。

本次实验中采用4:2:0色度取样格式。

实验素材

本次实验所用RGB文件的图片如下所示:
在这里插入图片描述
IDE使用visual studio 2019,使用easyx库显示图片。

实验结果

编写的程序运行结果如下所示:

  1. 原始图片
    在这里插入图片描述

  2. 转换至YUV空间后使用YUVViewer查看
    在这里插入图片描述

  3. 从YUV空间转换回RGB空间
    在这里插入图片描述

  4. 经过转换的文件与源文件的误差(像素误差值做平方处理)在这里插入图片描述

  5. 统计并导出各像素值的误差情况,用Excel绘图统计
    在这里插入图片描述
    在这里插入图片描述
    经图表可知,转换造成的误差值多分布在15以内。

误差分析

造成色彩空间转换的误差来源有:

  1. 计算误差,由上文转换公式可知,直接计算后的结果是一个浮点型数据,但实际存储和表示图片像素都是使用的8bit unsigned char型数据,舍弃小数点后小数造成误差。
  2. 保护边带造成的误差,在实际计算中YUV数据都会进入保护边带内,但根据转换要求需要将进入边带的数值更改为边界值,因此造成了转换误差。
  3. 色度采样误差,由于采用4:2:0色度采样格式,每四个像素共用一个色度采样值,因此导致了一部分色度信息的丢失,转换回RGB色彩空间时导致误差。

程序代码

其中,须向main函数传入参数 down.rgb 256 256
rgb_raw.h

#ifndef RGB_img
#define RGB_img
#pragma once

typedef unsigned char uint1;
typedef unsigned short uint2;
typedef unsigned int uint4;
typedef int int4;

#pragma pack(push)
#pragma pack(2)

typedef struct {
   
	uint1 b;
	uint1 g;
	uint1 r;
}BGR;

class RGB_raw_image
{
   
protected:
	int4 width;
	int4 height;
	int4 size;
	BGR* data;

#pragma pack(pop)

public:
	explicit RGB_raw_image(uint4 w, uint4 h);
	explicit RGB_raw_image(uint4 w, uint4 h, const char* path);
	RGB_raw_image(const RGB_raw_image& input);
	virtual ~RGB_raw_image();
	bool open(uint4 w, uint4 h, const char* path);
	bool save(const char* filePath);
	bool clear();
	uint4 get_size();
	uint4 get_width();
	uint4 get_height();
	BGR* get_pixel(uint4 x, uint4 y);
	BGR* get_data();
	void show();
	uint1 clamp_RGB(int t);
	RGB_raw_image operator -(const RGB_raw_image& input);
};
#endif

rgb_raw.cpp

#include "RGB_raw.h"
#include <iostream>
#include <fstream>
#include <conio.h>
#include <easyx.h>	//using easyx to draw picture
using namespace std;

uint1 RGB_raw_image::clamp_RGB(int t)
{
   
	if (t < 0) {
   
		return 0;
	}
	else if (t > 255) {
   
		return 255;
	}
	else {
   
		return t;
	}
}

RGB_raw_image::RGB_raw_image(uint4 w, uint4 h)
{
   
	width = w;
	height = h;
	size = width * height * 3;
	data = new BGR[width * height];
}

RGB_raw_image::RGB_raw_image(uint4 w, uint4 h, const char* path)
{
   
	try {
   
		data = NULL;
		if (!open(w, h, path)) {
   
			size = 0;
			throw("Fail to load file");
		}
	}
	catch (const char* msg) {
   
		cerr << msg << "  " << path << endl;
	}
}

RGB_raw_image::RGB_raw_image(const RGB_raw_image& input)
{
   
	this->width = input.width;
	this->height = input.height;
	this->size = input.size;
	this->data = new BGR[this->width * this->height];
	//copy the original data to destination object
	memcpy(this->data, input.data, this->size);
}

RGB_raw_image::~RGB_raw_image()
{
   
	delete[] data;
}

bool RGB_raw_image::open(uint4 w, uint4 h, const char* path)
{
   
	width = w;
	height = h;

	ifstream file_in(path, ios::binary);
	if (file_in.is_open())
	{
   
		istream::pos_type start_pos = file_in.tellg();
		file_in.seekg(0, ios_base::end);
		istream::pos_type end_pos = file_in.tellg();
		file_in.seekg(start_pos);
		istream::pos_type file_size = end_pos - start_pos;
		size = file_size;

		if (size != width * height * 3) {
   
			cerr << __func__ << "  The file's size is wrong!" << endl;
			return false;
		}
		else {
   
			data = new BGR[width * height];
			file_in.read((char*)data, size);
			return true;
		}
		file_in.close();
	}
	else {
   
		return false;
	}
}

bool RGB_raw_image::save(const char* filePath)
{
   
	std::ofstream file_out(filePath, ios::binary); //ios::binary means open a binary file.
	if (file_out.is_open()) {
   
		file_out.write((char*)data, size);
		file_out.close();
		return true;
	}
	else {
   
		cout << __func__ << "Failed to open output file" << filePath << endl;
		return false;
	}
}

bool RGB_raw_image::clear()
{
   
	delete[] data;
	width = 0;
	height = 0;
	size = 0;
	return true;
}

uint4 RGB_raw_image::get_size()
{
   
	return size;
}

uint4 RGB_raw_image::get_width()
{
   
	return width;
}

uint4 RGB_raw_image::get_height()
{
   
	return height;
}

BGR* RGB_raw_image::get_pixel(uint4 x, uint4 y)
{
   
	if (x < width && y < height) {
   
		uint4 index = y * width + x;
		return data + index;
	}
	else {
   
		cerr << __func__ << "Position out of size!" << endl;
		return NULL;
	}
}

BGR* RGB_raw_image::get_data()
{
   
	return data;
}

void RGB_raw_image::show()
{
   
	initgraph(width, height, SHOWCONSOLE);
	cout << "Loading..." << endl;
	BGR* bgr;
	for (int i = 0; i < height; i++) {
   
		for (int j = 0; j < width; j++) {
   
			bgr = this->get_pixel(j, i);
			putpixel(j, i, RGB(bgr->r, bgr->g, bgr->b));
		}
	}
	cout << "Finish" << endl;
}

RGB_raw_image RGB_raw_image::operator-(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值