三维重建:PNG格式详解-与LibPNG使用

PNG图像包含了骨骼信息,左边的图像比右边的大几十K,包含了骨骼信息:

                

PNG格式详解:https://blog.mythsman.com/post/5d2d62b4a2005d74040ef7eb/

LibPNG的使用:https://blog.csdn.net/dreamInTheWorld/article/details/55805901

一.PNG格式详解

概述

PNG是20世纪90年代中期开始开发的图像文件存储格式,其目的是替代GIF和TIFF文件格式,同时增加一些GIF文件格式所不具备的特性。流式网络图形格式(Portable Network Graphic Format,PNG)名称来源于非官方的“PNG’s Not GIF”,是一种位图文件(bitmap file)存储格式,读成“ping”。PNG用来存储灰度图像时,灰度图像的深度可多到16位,存储彩色图像时,彩色图像的深度可多到48位,并且还可存储多到16位的α通道数据。PNG使用从LZ77派生的无损数据压缩算法。(说白了这就是一种方便的、适于网络传播的轻便图片文件格式)

特性

  1. 使用调色板技术可支持256种颜色的彩色图像。(必须的)
  2. 流式读/写性(streamability):图像文件格式允许连续读出和写入图像数据。(因此适于网络传播)
  3. 逐次逼近显示(progressive display):这种特性可使在通信链路上传输图像文件的同时就在终端上显示图像,把整个轮廓显示出来之后逐步显示图像的细节,也就是先用低分辨率显示图像,然后逐步提高它的分辨率。(类似马赛克逐渐消除的过程)
  4. 透明性(transparency):这个性能可使图像中某些部分不显示出来,用来创建一些有特色的图像。
  5. 辅助信息(ancillary information):这个特性可用来在图像文件中存储一些文本注释信息。(就是可以说一些废话)
  6. 独立于计算机软硬件环境。
  7. 使用无损压缩。(无损!)
  8. 可在一个文件中存储多幅图像。

文件结构

PNG图像格式文件由文件署名和数据块(chunk)组成。

文件署名域

8字节的PNG文件署名域用来识别该文件是不是PNG文件。该域的值是:

十进制数十六进制数
13789
8050
784e
7147
130d
100a
261a
100a

这个文件署名就是在《利用文件头标志判断文件类型》中提到的文件头标志了,很简单。

数据块

这里有两种类型的数据块,一种是称为关键数据块(critical chunk),就是必须要有的块;另一种叫做辅助数据块(ancillary chunks)。

每个数据块都由下表所示的的4个域组成。

名称字节数说明
Length(长度)4字节指定数据块中数据域的长度,其长度不超过(231−1)(231−1)字节
Chunk Type Code(数据块类型码)4字节数据块类型码由ASCII字母(A-Z和a-z)组成
Chunk Data(数据块实际内容可变长度存储按照Chunk Type Code指定的数据
CRC(循环冗余检测4字节存储用来检测是否有错误的循环冗余码

其中CRC(cyclic redundancy check)域中的值是对Chunk Type Code域和Chunk Data域中的数据进行计算得到的,可以看做一种校验码。

关键数据块

关键数据块中的4个标准数据块是:

(1) 文件头数据块IHDR(header chunk):

它包含有PNG文件中存储的图像数据的基本信息,并要作为第一个数据块出现在PNG数据流中,而且一个PNG数据流中只能有一个文件头数据块。

文件头数据块由13字节,组成结构如下:

域的名称字节数说明
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或84:带α通道数据的灰度图像,8或16;6:带α通道数据的真彩色图像,8或16
Compression method1 byte压缩方法(LZ77派生算法)
Filter method1 byte滤波器方法
Interlace method1 byte隔行扫描方法:0:非隔行扫描;1: Adam7(由Adam M. Costello开发的7遍隔行扫描方法)

(2) 调色板数据块PLTE(palette chunk):

它包含有与索引彩色图像((indexed-color image))相关的彩色变换数据,它仅与索引彩色图像有关,而且要放在图像数据块(image data chunk)之前。真彩色的PNG数据流也可以有调色板数据块,目的是便于非真彩色显示程序用它来量化图像数据,从而显示该图像。结构如下:

|颜色|字节|意义|
|Red|1 byte||0 = 黑色, 255 = 红|
|Green|1 byte||0 = 黑色, 255 = 绿色|
|Blue|1 byte||0 = 黑色, 255 = 蓝色|

PLTE数据块是定义图像的调色板信息,PLTE可以包含1~256个调色板信息,每一个调色板信息由3个字节组成,因此调色板数据块所包含的最大字节数为768,调色板的长度应该是3的倍数,否则,这将是一个非法的调色板。

对于索引图像,调色板信息是必须的,调色板的颜色索引从0开始编号,然后是1、2……,调色板的颜色数不能超过色深中规定的颜色数(如图像色深为4的时候,调色板中的颜色数不可以超过2^4=16),否则,这将导致PNG图像不合法。

(3) 图像数据块IDAT(image data chunk):

它存储实际的数据,在数据流中可包含多个连续顺序的图像数据块。

IDAT存放着图像真正的数据信息,因此,如果能够了解IDAT的结构,我们就可以很方便的生成PNG图像。

(4) 图像结束数据IEND(image trailer chunk):

它用来标记PNG文件或者数据流已经结束,并且必须要放在文件的尾部。

如果我们仔细观察PNG文件,我们会发现,文件的结尾12个字符看起来总应该是这样的:

00 00 00 00 49 45 4E 44 AE 42 60 82

不难明白,由于数据块结构的定义,IEND数据块的长度总是0(00 00 00 00,除非人为加入信息),数据标识总是IEND(49 45 4E 44),因此,CRC码也总是AE 42 60 82。

最后,除了表示数据块开始的IHDR必须放在最前面, 表示PNG文件结束的IEND数据块放在最后面之外,其他数据块的存放顺序没有限制。

辅助数据块

(比较杂,不需要全部了解透)

PNG文件格式规范制定的10个辅助数据块是:

  1. 背景颜色数据块bKGD(background color)。
  2. 基色和白色度数据块cHRM(primary chromaticities and white point)。所谓白色度是指当R=G=B=最大值时在显示器上产生的白色度。
  3. 图像γ数据块gAMA(image gamma)。
  4. 图像直方图数据块hIST(image histogram)。
  5. 物理像素尺寸数据块pHYs(physical pixel dimensions)。
  6. 样本有效位数据块sBIT(significant bits)。
  7. 文本信息数据块tEXt(textual data)。
  8. 图像最后修改时间数据块tIME (image last-modification time)。
  9. 图像透明数据块tRNS (transparency)。
  10. 压缩文本数据块zTXt (compressed textual data)。

数据块摘要

关键数据块、辅助数据块和专用公共数据块(special-purpose public chunks)综合下表中:

数据块符号数据块名称多数据块可选否位置限制
IHDR文件头数据块第一块
cHRM基色和白色点数据块在PLTE和IDAT之前
gAMA图像γ数据块在PLTE和IDAT之前
sBIT样本有效位数据块在PLTE和IDAT之前
PLTE调色板数据块在IDAT之前
bKGD背景颜色数据块在PLTE之后IDAT之前
hIST图像直方图数据块在PLTE之后IDAT之前
tRNS图像透明数据块在PLTE之后IDAT之前
oFFs(专用公共数据块)在IDAT之前
pHYs物理像素尺寸数据块在IDAT之前
sCAL(专用公共数据块)在IDAT之前
IDAT图像数据块与其他IDAT连续
tIME图像最后修改时间数据块无限制
tEXt文本信息数据块无限制
zTXt压缩文本数据块无限制
fRAc(专用公共数据块)无限制
gIFg(专用公共数据块)无限制
gIFt(专用公共数据块)无限制
gIFx(专用公共数据块)无限制
IEND图像结束数据最后一个数据块

tEXt和zTXt数据块中的标准关键字:

关键字说明
Title图像名称或者标题
Author图像作者名
Description图像说明
Copyright版权声明
CreationTime原图创作时间
Software创作图像使用的软件
Disclaimer弃权
Warning图像内容警告
Source创作图像使用的设备
Comment各种注释

一个例子

为了便于研究,我在本地找了个24x24像素的图片:

 

用十六进制打开后是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
0000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452  .PNG........IHDR
0000010: 0000 0018 0000 0018 0806 0000 00e0 773d  ..............w=
0000020: f800 0000 1974 4558 7453 6f66 7477 6172  .....tEXtSoftwar
0000030: 6500 4164 6f62 6520 496d 6167 6552 6561  e.Adobe ImageRea
0000040: 6479 71c9 653c 0000 0344 4944 4154 78da  dyq.e<...DIDATx.
0000050: b454 4b48 5b51 10bd 792f 2646 a346 7411  .TKH[Q..y/&F.Ft.
0000060: 450b cac3 6840 8c14 0242 8542 e9aa ab42  E...h@...B.B...B
0000070: 5785 8614 c428 b4d0 5569 b108 5dbb 29b4  W....(..Ui..].).
0000080: 1b05 a5ab 6cb3 2a14 0ab5 8b42 75a3 d188  ....l.*....Bu...
0000090: 82c6 0ff1 4320 a2e2 2f7e 12ed 9c47 e671  ....C ../~...G.q
00000a0: 8d2f 8950 3a30 dcc7 bb33 67e6 cee7 5826  ./.P:0...3g...X&
00000b0: 2626 8499 288a a2ab d56a d555 55d5 57d7  &&..(....j.UU.W.
00000c0: d7d7 be6c 36fb fef2 f232 45a7 b8ba ba12  ...l6....2E.....
00000d0: c160 5014 13ab d94f 8bc5 72e3 24e0 1e9f  .`P....O..r.$...
00000e0: cff7 b9ae ae4e 4c4e 4eda 3299 4c00 777c  .....NLNN.2.L.w|
00000f0: 5f4c 1472 16f9 9a07 2e6a 6b6b 875a 5b5b  _L.r.....jkk.Z[[
0000100: 454d 4d8d e8ea ea7a 4eff 3ce2 8ea2 3018  EMM....zN.<...0.
0000110: 94cb c28a 7f04 765f d3b4 27ec d0d8 d8a8  ......v_..'.....
0000120: 5655 55bd 639b 9201 a8b6 a8af 516b 9bcd  VUU.c.......Qk..
0000130: 26ca caca f46f 0020 7bb7 db6d 38d8 ed76  &....o. {..m8..v
0000140: d1d1 d1f1 82ee 34d8 940c 0023 00c2 11e0  ......4....#....
0000150: 1c20 975d 2781 3d75 381c 379c 9a9b 9b55  . .]'.=u8.7....U
0000160: 2ad7 1012 bb73 0028 c073 1373 8f02 3c68  *....s.(.s.s..<h
0000170: 6b6b fb44 25b9 e554 5e5e 2eba bbbb f18a  kk.D%..T^^......
0000180: c791 4844 2b16 c012 0e87 fd04 faac bebe  ..HD+...........
0000190: be85 cad1 e272 b97c 04a0 e245 4ea7 530f  .....r.|...EN.S.
00001a0: 5c48 8e8f 8f45 3a9d d6f5 f0f0 7091 747b  \H...E:.....p.t{
00001b0: 7777 779d feff a649 0b07 0281 acb5 baba  www....I........
00001c0: fa97 dfef 7710 7041 309e 2c79 ba20 4800  ....w.pA0.,y. H.
00001d0: 9a13 2f81 7a11 7479 79b9 3f16 8ba1 ae63  ../.z.tyy.?....c
00001e0: caf9 f979 120b 83c5 81e6 03e3 8e4f 59e5  ...y.........OY.
00001f0: a010 5e3c f405 2705 dbd6 7b90 4aa5 1ecd  ..^<..'...{.J...
0000200: cdcd 250e 0e0e c4c5 c585 a080 fa49 0686  ..%..........I..
0000210: 1303 ca81 7007 1bf6 c1f7 d9d9 9958 5b5b  ....p........X[[
0000220: 1384 f73a 140a 7de3 3d58 27e9 8d46 a371  ...:..}.=X'..F.q
0000230: 0491 c14a 89fc 1204 01f8 d4d4 1461 87be  ...J.........a..
0000240: e46f 7262 6363 a377 6666 6671 6f6f cfc8  .orbcc.wfffqoo..
0000250: dc6c cb65 651b 3439 1e8f 03bc 8fc0 c70a  .l.ee.49........
0000260: 5145 3291 483c 5c58 5888 a251 a5c0 5989  QE2.H<\XX..Q..Y.
0000270: f8c4 d6d6 5676 7a7a fae5 c0c0 c0f8 ad3d  ....Vvzz.......=
0000280: c823 ac14 3df3 c3d1 d191 d1e4 620a 5f94  .#..=.......b._.
0000290: 6673 7333 3238 38f8 b520 9be6 3887 ff79  fss3288.. ..8..y
00002a0: 989b f85f 7e3f e4a4 b0f1 74af 1525 3b36  ..._~?....t..%;6
00002b0: 8423 ed45 6745 4585 012c 4f92 3c41 fc02  .#.EgEE..,O.<A..
00002c0: d008 ed90 7774 74d4 5694 4df9 248e f182  ....wtt.V.M.$...
00002d0: 3278 4ccd 9aca 8161 039f caca 4a80 9b52  2xL....a....J..R
00002e0: b855 1e49 3a55 32f6 c209 60bc 4068 3aa6  .U.I:U2...`.@h:.
00002f0: 8b1a 9aa6 041c 4429 3a6f 7150 3000 9d9d  ......D):oqP0...
0000300: 641e 33ed 8114 a485 0238 4178 703c 3939  d.3......8Axp<99
0000310: 11c9 6452 acac acfc 248e 798b 9d21 b0e1  ..dR....$.y..!..
0000320: f6f6 f6fe a6a6 2683 5ec0 5b94 948f eec3  ......&.^.[.....
0000330: 055f c001 3015 5838 64bc baba 1add d9d9  ._..0.X8d.......
0000340: 01f0 0f76 a0cd 7d33 3f3f 3f42 73ff d1e3  ...v..}3???Bs...
0000350: f104 1b1a 1a54 f890 6805 a748 9a92 3fb4  .....T..h..H..?.
0000360: e6df 691f dcfb fbfb 2366 19e5 64fb f4f4  ..i.....#f..d...
0000370: b46f 7676 7664 6969 6998 5ed1 43fd 1837  .ovvvdiii.^.C..7
0000380: a5eb bb50 c2bf 8822 feb3 fc15 6000 74fe  ...P..."....`.t.
0000390: 7622 c159 82da 0000 0000 4945 4e44 ae42  v".Y......IEND.B
00003a0: 6082 0a                                  `..

接下来我们试着分析一下:

首先是八个字节的文件头标志,标识着png文件:

1
8950 4e47 0d0a 1a0a

 

接下来的地方就是IHDR数据块了:

0000 000d说明IHDR头块长为13

4948 4452IHDR标识(ascii码为IHDR)

下面是IHDR数据块的实际内容

0000 0018图像的宽,24像素

0000 0018图像的高,24像素

08 表示色深,这里是2^8=256,即这是一个256色的图像

06 颜色类型,查表可知这是带α通道数据的真彩色图像

00 PNG Spec规定此处总为0(非0值为将来使用更好的压缩方法预留),表示使压缩方法(LZ77派生算法)

00 同上

00 非隔行扫描

e0 773d f8 CRC校验

以上分析了第一个IHDR块的内容,其他块的分析方法类似,比如接下来的就是tEXt块了,很简单,不做分析了。(当然这里还有重要的IDAT块,这是图像的实际内容)

最后得有个IEND数据块,这部分正如上所说,通常都应该是

00 00 00 00 49 45 4E 44 AE 42 60 82

 

二.LibPNG使用

LibPNG是一款C语言编写的比较底层的读写PNG文件的跨平台的库。借助它,你可以轻松读写PNG文件的每一行像素。
因为PNG文件是经过压缩而且格式复杂的图形文件(有的PNG文件甚至像GIF文件一样带动画效果)
而且PNG可以是带透明通道的真彩色图像、不带透明通道的真彩色图像、索引颜色、灰度颜色等各种格式,如果大家都自己写程序分析PNG文件就会显得很麻烦、很累。因此,通过使用libpng你就能直接使用现成的函数、程序来读写PNG文件了。

使用方法:到libPNG的官方网站下载源代码,使用cmake配置之后,使用VS或者make工具编译成库;

                  之前需要使用合适的zlib源代码在相应的开发平台上进行编译。

参考:https://blog.csdn.net/dreamInTheWorld/article/details/55805901

  

两个图片的大小不一样;

 左边的比右边的多几十k的骨骼信息。

读取png代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../png.h"
#include "../pngstruct.h"
#include "../pnginfo.h"


#define PNG_BYTES_TO_CHECK	8
#define HAVE_ALPHA			1
#define NOT_HAVE_ALPHA		0
#define PNG_TEXT_SUPPORTED

typedef struct _pic_data pic_data;
struct _pic_data {
	int width, height; 	//长宽
	int bit_depth; 	   	//位深度
	int alpha_flag;		//是否有透明通道
	unsigned char *rgba;//实际rgb数据
};

int check_is_png(FILE **fp, const char *filename) //检查是否png文件
{
	char checkheader[PNG_BYTES_TO_CHECK]; //查询是否png头
	*fp = fopen(filename, "rb");
	if (*fp == NULL) {
		printf("open failed ...1\n");
		return -1;
	}
	if (fread(checkheader, 1, PNG_BYTES_TO_CHECK, *fp) != PNG_BYTES_TO_CHECK) //读取png文件长度错误直接退出
		return 0;
	return png_sig_cmp(checkheader, 0, PNG_BYTES_TO_CHECK); //0正确, 非0错误
	//return 0;//wishchin!!!
}

int decode_png(const char *filename, pic_data *out) //取出png文件中的rgb数据
{
	png_structp png_ptr; //png文件句柄
	png_infop	info_ptr;//png图像信息句柄
	int ret;
	FILE *fp;
	if (check_is_png(&fp, filename) != 0) {
		printf("file is not png ...\n");
		return -1;
	}
	printf("launcher[%s] ...\n", PNG_LIBPNG_VER_STRING); //打印当前libpng版本号

	//1: 初始化libpng的数据结构 :png_ptr, info_ptr
	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	info_ptr = png_create_info_struct(png_ptr);

	//2: 设置错误的返回点
	setjmp(png_jmpbuf(png_ptr));
	rewind(fp); //等价fseek(fp, 0, SEEK_SET);

	//3: 把png结构体和文件流io进行绑定 
	png_init_io(png_ptr, fp);
	//4:读取png文件信息以及强转转换成RGBA:8888数据格式
	//png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0); //读取文件信息
	png_read_info(png_ptr, info_ptr);
	int channels, color_type;
	channels = png_get_channels(png_ptr, info_ptr); //通道数量
	color_type = png_get_color_type(png_ptr, info_ptr);//颜色类型
	out->bit_depth = png_get_bit_depth(png_ptr, info_ptr);//位深度	
	out->width = png_get_image_width(png_ptr, info_ptr);//宽
	out->height = png_get_image_height(png_ptr, info_ptr);//高

	
	//png_const_structrp png_ptr,
	//	png_inforp info_ptr, 
	png_textp *text_ptr = NULL;// int *num_text;
	int num_text= png_get_text(png_ptr, info_ptr, text_ptr, NULL);//额外文本信息?

	//if(color_type == PNG_COLOR_TYPE_PALETTE)
	//	png_set_palette_to_rgb(png_ptr);//要求转换索引颜色到RGB
	//if(color_type == PNG_COLOR_TYPE_GRAY && out->bit_depth < 8)
	//	png_set_expand_gray_1_2_4_to_8(png_ptr);//要求位深度强制8bit
	//if(out->bit_depth == 16)
	//	png_set_strip_16(png_ptr);//要求位深度强制8bit
	//if(png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS))
	//	png_set_tRNS_to_alpha(png_ptr);
	//if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
	//	png_set_gray_to_rgb(png_ptr);//灰度必须转换成RG
	printf("channels = %d color_type = %d bit_depth = %d width = %d height = %d ...\n",
		channels, color_type, out->bit_depth, out->width, out->height);
	//info_ptr->text->//读不出数据,是函数用错了???

	int i, j, k;
	int size, pos = 0;
	int temp;

	//5: 读取实际的rgb数据
	png_bytepp row_pointers; //实际存储rgb数据的buf
	row_pointers = png_get_rows(png_ptr, info_ptr); //也可以分别每一行获取png_get_rowbytes();
	size = out->width * out->height; //申请内存先计算空间
	if (channels == 4 || color_type == PNG_COLOR_TYPE_RGB_ALPHA) { //判断是24位还是32位
		out->alpha_flag = HAVE_ALPHA; //记录是否有透明通道
		size *= (sizeof(unsigned char) * 4); //size = out->width * out->height * channel
		out->rgba = (png_bytep)malloc(size);
		if (NULL == out->rgba) {
			printf("malloc rgba faile ...\n");
			png_destroy_read_struct(&png_ptr, &info_ptr, 0);
			fclose(fp);
			return -1;
		}
		//从row_pointers里读出实际的rgb数据出来
		temp = channels - 1;
		for (i = 0; i < out->height; i++)
			for (j = 0; j < out->width * 4; j += 4)
				for (k = temp; k >= 0; k--)
					out->rgba[pos++] = row_pointers[i][j + k];
	}
	else if (channels == 3 || color_type == PNG_COLOR_TYPE_RGB) { //判断颜色深度是24位还是32位
		out->alpha_flag = NOT_HAVE_ALPHA;
		size *= (sizeof(unsigned char) * 3);
		out->rgba = (png_bytep)malloc(size);
		if (NULL == out->rgba) {
			printf("malloc rgba faile ...\n");
			png_destroy_read_struct(&png_ptr, &info_ptr, 0);
			fclose(fp);
			return -1;
		}
		//从row_pointers里读出实际的rgb数据
		temp = (3 * out->width);
		for (i = 0; i < out->height; i++) {
			for (j = 0; j < temp; j += 3) {
				out->rgba[pos++] = row_pointers[i][j + 2];
				out->rgba[pos++] = row_pointers[i][j + 1];
				out->rgba[pos++] = row_pointers[i][j + 0];
			}
		}
	}
	else return -1;
	//6:销毁内存
	png_destroy_read_struct(&png_ptr, &info_ptr, 0);
	fclose(fp);
	//此时, 我们的out->rgba里面已经存储有实际的rgb数据了
	//处理完成以后free(out->rgba)
	return 0;
}

结果,依然未能读取PNG的额外信息....!!!悲剧!

使用方式:
 

int CPngWish::readpng(int argc, char* pngfilename, char* filename2)
{
    pic_data out;
    //if (argc == 3) {
    //test_one_file2(pngfilename, filename2);
    decode_png(pngfilename, &out);
    write_png_file(filename2, &out);
    free(out.rgba);
    //}
    //else {
    //    puts("please input two file, \nargv[1]:source.png argv[2]:dest.png");
    //}

    return 1;
};

有待深入研究...

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值