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

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

程序员文章站 2022-07-14 22:10:35
...

实验目的

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

实验原理

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

实验结果

实验素材:
数据压缩学习实验(三)PNG文件读取及转换至YUV色彩空间的C++实现
文件信息读取结果:
数据压缩学习实验(三)PNG文件读取及转换至YUV色彩空间的C++实现
使用easyx库绘制的图像:
数据压缩学习实验(三)PNG文件读取及转换至YUV色彩空间的C++实现
转换后YUV图像播放结果:
数据压缩学习实验(三)PNG文件读取及转换至YUV色彩空间的C++实现

总结与误差分析

  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));
		}
	}
}

相关标签: c++