数据压缩学习实验(三)PNG文件读取及转换至YUV色彩空间的C++实现
程序员文章站
2022-07-14 22:10:35
...
实验目的
- 学习PNG文件格式,为随后学习图像的压缩做准备。
- 编程读取PNG文件中的外围解析,使用lodepng库解码被压缩的图像内容。
- 将文件内容转换至YUV色彩空间。
实验原理
- PNG文件格式初步了解:数据压缩学习笔记(二)以PNG为例探讨设计思想和理解
- YUV色彩空间的转换:数据压缩学习实验(一)RGB与YUV色彩空间转换的C++实现及误差分析
- 使用lodepng库对压缩图像进行解码:LodePNG
- 使用easyx库绘制解码后的图像:EasyX
实验结果
实验素材:
文件信息读取结果:
使用easyx库绘制的图像:
转换后YUV图像播放结果:
总结与误差分析
- 由于该程序执行的仍然是RGB色彩空间向YUV 4:2:0的转换,因此转换部分误差情况与之前RGB转YUV实验误差情况相同。
- 通过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));
}
}
}