PNG文件转为YUV文件的C实现

核心思路

核心思路是找到图像数据部分,根据调色板,找到数据部分后解码,处理为rgb后利用先前的rgb2yuv子函数去处理。

具体实现

首部处理

png文件开头8个字节为固定字节。目的是区分于其他文件,表明本文件是,其中第一个字节0x89超出了ASCII字符的范围,这是为了避免某些软件将PNG文件当做文本文件来处理。

十进制137 80 78 71 13 10 26 10
十六进制数89 50 4E 47 0D 0A 1A 0A

开头读出文件前八个字节,验明正身。c语言以16进制输出时略去了高字节的0。
在这里插入图片描述

	fread(head, 1, 8, pngFile);
	for (int i = 0; i < 8; i++) {
		printf("%x",*(head+i));//输出头八个字节判断是否为png文件,应为89504E470D0A1A0A

	}

数据块拆解

由之前的了解我们可以知道png文件格式定义了两种类型的数据块,一种是称为关键数据块(critical chunk),这是标准的数据块,另一种叫做辅助数据块(ancillary chunks),这是可选的数据块。这一步就是类似于ts包分析,由于所有的chunk都具有共同特征结构如下

chunk长度数据块名称数据CRC校验码
字节44待定4

由于结构已知,我们可以统计数据长度和名称,并将每个块数据部分存入对应的缓存区,使用指针数组实现。之前定义了chunkcontent数组用来存放指针

	for (int i = 0; i < 1000; i++) {
		unsigned length = 0;
		unsigned char* crc = NULL;
		crc = (unsigned char*)malloc(4);
		length = assesschunk(pngFile);
		if (length == 0) {
			break;
		}
		chunkcontent[i]= (unsigned char*)malloc(length);
		fread(chunkcontent[i], 1, length, pngFile);
		fread(crc, 1, 4, pngFile);
		

	}

子函数调用

在分析数据块部分调用了子函数,输出块的名称及长度,返回长度值,方便读入缓存区。


```c
unsigned int assesschunk(FILE*filename) {
	unsigned char* chunklength = NULL;
	
	unsigned int length;
	chunklength = (unsigned char*)malloc(4);
	fread(chunklength, 1, 4, filename);
		length = unsigned int(*(chunklength)) * 16777216 + unsigned int(*(chunklength + 1)) * 65536 + unsigned int(*(chunklength + 2)) * 256 + unsigned int(*(chunklength + 3));
		printf("\n");
		printf("%u\n", length);//判断长度
		char* chunktype = NULL;
		chunktype = ( char*)malloc(4);
		fread(chunktype, 1, 4, filename);
		for (int i = 0; i < 4; i++) {
			
			putchar(*(chunktype + i));
		}//判断类型
		//chunkcontent_inside = buf;
		//chunkcontent_inside = (unsigned char*)malloc(length);
		//fread(chunkcontent_inside, 1, length, filename);
		//忽略crc,指针指向下一个chunk的头部
		return(length);
}

数据块分析

IHDR文件头数据块

名称长度功能
Width4 bytes图像宽度,以像素为单位
Height4 bytes图像高度,以像素为单位
Bit depth1 byte图像深度:
索引彩色图像:1,2,4或8
灰度图像:1,2,4,8或16
真彩色图像:8或16
ColorType1 byte颜色类型:
0:灰度图像, 1,2,4,8或16
2:真彩色图像,8或16
3:索引彩色图像,1,2,4或8
4:带α通道数据的灰度图像,8或16
6:带α通道数据的真彩色图像,8或16
Compression method1 byte压缩方法(LZ77派生算法)
Filter method1 byte滤波器方法
Interlace method1 byte隔行扫描方法:
0:非隔行扫描
1: Adam7(由Adam M. Costello开发的7遍隔行扫描方法)

由于规定要求IDHR是第一个数据块,长度一致,正好可以测试我们的chunkassess函数是否正常。
读出本次使用的png文件有IHDR数据块长度13字节,还有一个phys数据块是物理像素尺寸数据块,我们这次实验用不到,先不管他。
在这里插入图片描述
在这里插入图片描述后续输出显示本次实验还有27个IDAT数据块用于存放压缩后的图像数据,其中26个满长度,长度为8192个字节,最后一个不满长度为3758个字节。最后是标志着图像结束的IEND数据块。

分析IHDR数据块

读取数据后,进行图像数据的分析,采用结构体的方式。在结构体内写子函数负责结构体成员的赋值与输出。

struct ihdr {
	unsigned int width, height;
	unsigned char Bit_depth, ColorType, Compression_method, Filter_method, Interlace_method;
	void ihdr_value(unsigned char* content) {
		width = unsigned int(*(content)) * 16777216 + unsigned int(*(content + 1)) * 65536 + unsigned int(*(content + 2)) * 256 + unsigned int(*(content + 3));
		height = unsigned int(*(content+4)) * 16777216 + unsigned int(*(content + 5)) * 65536 + unsigned int(*(content + 6)) * 256 + unsigned int(*(content + 7));
		Bit_depth = *(content + 8);
		ColorType = *(content + 9);
		Compression_method = *(content + 10);
		Filter_method = *(content + 11);
		Interlace_method = *(content + 12);
	}
	void show() {
		printf(" width, height, Bit_depth, ColorType, Compression_method, Filter_method, Interlace_method\n");
		printf("%d,%d,%d,%d,%d,%d,%d\n", width, height, Bit_depth, ColorType, Compression_method, Filter_method, Interlace_method);
	}
}ihdr1;

输出结果如下在这里插入图片描述
表示2084*2084像素,8表示为真彩色图像,6表示此图片为带a数据通道的真彩色图像。这也是为什么没有找到调色板数据块的原因。000表示压缩方法,滤波方法皆为默认,隔行扫描为无。

IDAT图像数据块

理论上IDAT数据块的数据是按顺序排列在27个数据块中的,我们将它按顺序全部读入一个缓冲文件中,等待解压。

unsigned char* IDATdata= (unsigned char*)malloc(8192*26+3758);
	FILE* idatFile = NULL;
	idatFile = fopen("idat.txt", "wb");
	if (!idatFile)
		printf("open idat fail");
	else          
		printf("create idat success\n");//暂时存放idat
	for (int i = 2; i < 28; i++) {
		fwrite(chunkcontent[i], 1, 8192, idatFile);
	}
	fwrite(chunkcontent[28], 1,3758, idatFile);

解压数据

根据查询,png图像文件采用的LZ77压缩算法可以使用zlib库中的UNcompress函数进行解压,接下来进行库的编译和使用。
在官网下载完zlib库后,进行编译,得到lib文件。动态链接主要进行下面三步后就可以使用compress函数了。

  • 添加工程的头文件目录:工程—属性—配置属性—c/c+±–常规—附加包含目录:加上头文件存放目录。
  • 添加文件引用的lib静态库路径:工程—属性—配置属性—链接器—常规—附加库目录:加上lib文件存放目录。
  • 然后添加工程引用的lib文件名:工程—属性—配置属性—链接器—输入—附加依赖项:加上lib文件名
fwrite(chunkcontent[28], 1,3758, idatFile);
	fread(IDATdata, 1, 8192 * 26 + 3758, idatFile);
	unsigned char* newdata = (unsigned char*)malloc(width*height *4);
	uLongf newlength = width * height * 4;
	uncompress(newdata, &(newlength), IDATdata, 8192 * 26 + 3758);

24位真彩色+a通道理论上是一个像素4个字节,我们解压缩的空间因此暂定为width * height * 4。

rgb文件转换为yuv文件

解压后是rgb+alpha的32位格式,我们要写一个a通道子函数处理得到不带透明度信息的rgb文件。如果直接舍弃a通道,输出的rgb图如下:
图片因为yuv查看器的原因是倒着的
图片因为yuv查看器的原因是倒着的,说明解压缩的数据应该是无误的,但颜色完全没有了,可能与a通道数据丢失有关,考虑根据
output = alpha * foreground + (1-alpha) * background计算复合的rgb信号,未完

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值