数据压缩学习实验(三)PNG文件读取及转换至YUV色彩空间的C++实现

实验目的

  1. 学习PNG文件格式,为随后学习图像的压缩做准备。
  2. 编程读取PNG文件中的外围解析,使用lodepng库解码被压缩的图像内容。
  3. 将文件内容转换至YUV色彩空间。

实验原理

  1. PNG文件格式初步了解:数据压缩学习笔记(二)以PNG为例探讨设计思想和理解
  2. YUV色彩空间的转换:数据压缩学习实验(一)RGB与YUV色彩空间转换的C++实现及误差分析
  3. 使用lodepng库对压缩图像进行解码:LodePNG
  4. 使用easyx库绘制解码后的图像:EasyX

实验结果

实验素材:
在这里插入图片描述
文件信息读取结果:
在这里插入图片描述
使用easyx库绘制的图像:
在这里插入图片描述
转换后YUV图像播放结果:
在这里插入图片描述

总结与误差分析

  1. 由于该程序执行的仍然是RGB色彩空间向YUV 4:2:0的转换,因此转换部分误差情况与之前RGB转YUV实验误差情况相同。
  2. 通过VScode软件查看二进制发现在PNG文件中,各项数据使用大端排序(big-endian),即高位字节在低地址处,这与之前学习的BMP文件使用的小端排序不同,因此读取方法需要做相应修改。

程序代码

YUV文件代码 “YUV_RAW.h” , “YUW_RAW.cpp” 见数据压缩学习实验(一)RGB与YUV色彩空间转换的C++实现及误差分析

converter.h

#ifndef tr
#define tr
#include "YUV_raw.h"
#include <vector>
using namespace std;

void init_LookupTable();
uint1 clamp_Y(int t);
uint1 clamp_UV(int t);
uint1 clamp_RGB(int t);
bool png2yuv(std::vector<unsigned char> data, uint4 w, uint4 h, YUV_raw_image& yuv, uint4 frame = 0);

#endif

converter.cpp

#include "converter.h"
#include <iostream>
#include <fstream>
using namespace std;

static uint1 RGBYUV02290[256];
static uint1 RGBYUV05870[256];
static uint1 RGBYUV01140[256];
static uint1 RGBYUV01684[256];
static uint1 RGBYUV03316[256];
static uint1 RGBYUV05000[256];
static uint1 RGBYUV04187[256];
static uint1 RGBYUV00813[256];
static int YUVRGB14020[256];
static int YUVRGB07141[256];
static int YUVRGB03144[256];
static int YUVRGB17720[256];
static bool init_flg = false;

void init_LookupTable()
{
	if (init_flg == false) {
		for (int i = 0; i < 256; i++) {
			RGBYUV02290[i] = (float)i * 0.299;
			RGBYUV05870[i] = (float)i * 0.587;
			RGBYUV01140[i] = (float)i * 0.114;
			RGBYUV01684[i] = (float)i * 0.1684;
			RGBYUV03316[i] = (float)i * 0.3316;
			RGBYUV05000[i] = (float)i * 0.5;
			RGBYUV04187[i] = (float)i * 0.4187;
			RGBYUV00813[i] = (float)i * 0.0813;
			YUVRGB14020[i] = (float)(i - 128) * 1.402;
			YUVRGB07141[i] = (float)(i - 128) * 0.71414;
			YUVRGB03144[i] = (float)(i - 128) * 0.314414;
			YUVRGB17720[i] = (float)(i - 128) * 1.772;
		}
		init_flg = true;
	}
}

uint1 clamp_Y(int t)
{
	if (t < 16) {
		return 16;
	}
	else if (t > 235) {
		return 235;
	}
	else {
		return t;
	}
}

uint1 clamp_UV(int t)
{
	if (t < 16) {
		return 16;
	}
	else if (t > 240) {
		return 240;
	}
	else {
		return t;
	}
}

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

bool png2yuv(std::vector<unsigned char> data, uint4 w, uint4 h, YUV_raw_image& yuv, uint4 frame)
{
	if (init_flg == false) {
		init_LookupTable();
	}
	int width = w;
	int height = h;
	chrominace_mode mode = yuv.get_mode();
	if (yuv.get_width() != width || yuv.get_height() != height) {
		cerr << __func__ << "\t The size of yuv file doesn\t match!" << endl;
		return false;
	}
	int y, u, v;
	int r, g, b;
	YUV* yuv_data = yuv.get_data();
	/*loop to traverse all pixels*/
	int loop_y(0), loop_uv(0);
	for (int loop_i = 0; loop_i < height; loop_i++) {
		for (int loop_j = 0; loop_j < width; loop_j++) {
			r = data[4 * loop_i * w + 4 * loop_j + 0];
			g = data[4 * loop_i * w + 4 * loop_j + 1];
			b = data[4 * loop_i * w + 4 * loop_j + 2];
			y = RGBYUV02290[r] + RGBYUV05870[g] + RGBYUV01140[b];
			u = RGBYUV05000[b] - RGBYUV01684[r] - RGBYUV03316[g] + 128;
			v = RGBYUV05000[r] - RGBYUV04187[g] - RGBYUV00813[b] + 128;
			y = clamp_Y(y);
			u = clamp_UV(u);
			v = clamp_UV(v);
			switch (mode)
			{
			case(C444): {
				*(yuv_data->Y_data + loop_y) = y;
				*(yuv_data->U_data + loop_y) = u;
				*(yuv_data->V_data + loop_y) = v;
			}break;
			case(C422): {
				*(yuv_data->Y_data + loop_y) = y;
				if (loop_y % 2 == 0) {
					*(yuv_data->U_data + loop_uv) = u;
					*(yuv_data->V_data + loop_uv) = v;
					loop_uv++;
				}
			}break;
			case(C420): {
				*(yuv_data->Y_data + loop_y) = y;
				if ((loop_y % 2 == 0) && (((loop_y - (loop_y % width)) / width) % 2 == 0)) {
					/*this condition means the position of chrominace sample*/
					*(yuv_data->U_data + loop_uv) = u;
					*(yuv_data->V_data + loop_uv) = v;
					loop_uv++;
				}
			}break;
			case(C411): {
				*(yuv_data->Y_data + loop_y) = y;
				if (loop_y % 4 == 0) {
					*(yuv_data->U_data + loop_uv) = u;
					*(yuv_data->V_data + loop_uv) = v;
					loop_uv++;
				}
			}break;
			}
			loop_y++;  //Important!!!!!!!!!!!!!!!!
		}
	}
	return true;
}

main.cpp

#include "lodepng.h"  // using lodepng to decode
#include "YUV_raw.h"
#include "converter.h"
#include <easyx.h>  // using easyx to show image
#include <conio.h>  // using easyx to show image
#include <math.h>
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;

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

struct IHDR {
	uint4 length;
	uint4 type_code;
	uint4 width;
	uint4 height;
	uint1 depth;
	uint1 color_type;
	uint1 compression;
	uint1 filter;
	uint1 interlace;
};

uint4 B2D(uint1 input[4]);
bool find_IHDR(ifstream& file_in, IHDR& ihdr);
bool find_IDAT(ifstream& file_in);
bool find_IEND(ifstream& file_in);
void display(std::vector<unsigned char>data, uint4 w, uint4 h);

int main(void)
{
	//load file
	const char* path = "./down.png";
	std::ifstream file_in(path,ios::binary);

	//data pf png image
	IHDR ihdr;
	std::vector<unsigned char> data;

	//seek hdrl chunk and get information
	if (find_IHDR(file_in, ihdr))
	{
		cout << "Find IHDR chunk, details: " << endl;
		cout << "width:" << (int)ihdr.width << endl;
		cout << "height:" << (int)ihdr.height << endl;
		cout << "depth:" << (int)ihdr.depth << endl;
		cout << "color type:" << (int)ihdr.color_type << endl;
		cout << "compression method:" << (int)ihdr.compression << endl;
		cout << "filter method:" << (int)ihdr.filter << endl;
		cout << "interlace method:" << (int)ihdr.interlace << endl << endl;
	}
	else {
		cout << "Can't find IHDR chunk!" << endl;
		exit(1);
	}
	if (find_IDAT(file_in)) {
		cout << "Find IDAT chunk!" << endl;
	}
	else {
		cout << "Can't find IDAT chunk!" << endl;
	}

	if (find_IEND(file_in)) {
		cout << "Find IEND chunk!" << endl;
		cout << "Starting decode..." << endl;
		unsigned w(ihdr.width), h(ihdr.height);
		char err = lodepng::decode(data, w, h, path);
		if (err) {
			cout << "Can not decode png file, error code: " << err << endl;
		}
		else {
			cout << "Decoding finished!" << endl;
		}
	}
	else {
		cout << "Can't find IEND chunk!" << endl;
	}

	//close png file
	file_in.close();
	
	//conver to 4:2:0 YUV file
	YUV_raw_image yuv(ihdr.width, ihdr.height, 1, C420);
	png2yuv(data, ihdr.width, ihdr.height, yuv);
	yuv.save("./result.yuv");
	
	//show image
	display(data, ihdr.width, ihdr.height);
	system("Pause");
	closegraph();
	return 0;
}

uint4 B2D(uint1 input[4])
{
	/* Bytes to DWORD*/
	uint4 value = 0;
	for (int i = 0; i < 4; i++) {
		value += input[i] * (pow(256, 3 - i));
	}
	return value;
}

bool find_IHDR(ifstream& file_in, IHDR& ihdr)
{
	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);
	uint4 size = end_pos - start_pos - 4;

	for (int i = 0; i < size ; i++) {
		uint1 temp[4];
		file_in.read(reinterpret_cast<char*>(temp), 4);
		file_in.seekg(-3, ios::cur);
		if (B2D(temp) == 0x49484452)
		{
			file_in.seekg(-5, ios::cur);
			break;
		}
		if (i == size - 1) {
			return false;
		}
	}

	uint1 temp4[4];
	uint1 temp1[1];
	file_in.read(reinterpret_cast<char*>(temp4), 4);
	ihdr.length = B2D(temp4);
	file_in.read(reinterpret_cast<char*>(temp4), 4);
	ihdr.type_code = B2D(temp4);
	file_in.read(reinterpret_cast<char*>(temp4), 4);
	ihdr.width = B2D(temp4);
	file_in.read(reinterpret_cast<char*>(temp4), 4);
	ihdr.height = B2D(temp4);
	file_in.read(reinterpret_cast<char*>(temp1), 1);
	ihdr.depth = *temp1;
	file_in.read(reinterpret_cast<char*>(temp1), 1);
	ihdr.color_type = *temp1;
	file_in.read(reinterpret_cast<char*>(temp1), 1);
	ihdr.compression = *temp1;
	file_in.read(reinterpret_cast<char*>(temp1), 1);
	ihdr.filter = *temp1;
	file_in.read(reinterpret_cast<char*>(temp1), 1);
	ihdr.interlace = *temp1;
	file_in.seekg(start_pos);
	return true;
}

bool find_IDAT(ifstream& file_in)
{
	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);
	uint4 size = end_pos - start_pos - 4;

	for (int i = 0; i < size; i++) {
		uint1 temp[4];
		file_in.read(reinterpret_cast<char*>(temp), 4);
		file_in.seekg(-3, ios::cur);
		if (B2D(temp) == 0x49444154)
		{
			file_in.seekg(-5, ios::cur);
			break;
		}
		if (i == size - 1) {
			return false;
		}
	}
	file_in.seekg(start_pos);
	return true;
}

bool find_IEND(ifstream& file_in)
{
	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);
	uint4 size = end_pos - start_pos - 4;

	for (int i = 0; i < size; i++) {
		uint1 temp[4];
		file_in.read(reinterpret_cast<char*>(temp), 4);
		file_in.seekg(-3, ios::cur);
		if (B2D(temp) == 0x49454e44)
		{
			file_in.seekg(-5, ios::cur);
			break;
		}
		if (i == size - 1) {
			return false;
		}
	}
	file_in.seekg(start_pos);
	return true;
}

void display(std::vector<unsigned char> data, uint4 w, uint4 h)
{
	initgraph(w, h, SHOWCONSOLE);
	int r, g, b;
	for (int i = 0; i < h; i++) {
		for (int j = 0; j < w; j++) {
			r = data[4 * i * w + 4 * j + 0];
			g = data[4 * i * w + 4 * j + 1];
			b = data[4 * i * w + 4 * j + 2];
			putpixel(j, i, RGB(r, g, b));
		}
	}
}

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值