三维重建: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派生的無損數據壓縮算法。(說白了這就是一種方便的、適于網絡傳播的輕便圖片文件格式)
特性
文件結構
PNG圖像格式文件由文件署名和數據塊(chunk)組成。
文件署名域
8字節的PNG文件署名域用來識別該文件是不是PNG文件。該域的值是:
| 137 | 89 |
| 80 | 50 |
| 78 | 4e |
| 71 | 47 |
| 13 | 0d |
| 10 | 0a |
| 26 | 1a |
| 10 | 0a |
這個文件署名就是在《利用文件頭標志判斷文件類型》中提到的文件頭標志了,很簡單。
數據塊
這里有兩種類型的數據塊,一種是稱為關鍵數據塊(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字節,組成結構如下:
| Width | 4 bytes | 圖像寬度,以像素為單位 |
| Height | 4 bytes | 圖像高度,以像素為單位 |
| Bit depth | 1 byte | 圖像深度:索引彩色圖像:1,2,4或8 ;灰度圖像:1,2,4,8或16 ;真彩色圖像:8或16 |
| ColorType | 1 byte | 顏色類型:0:灰度圖像, 1,2,4,8或16;2:真彩色圖像,8或16;3:索引彩色圖像,1,2,4或84:帶α通道數據的灰度圖像,8或16;6:帶α通道數據的真彩色圖像,8或16 |
| Compression method | 1 byte | 壓縮方法(LZ77派生算法) |
| Filter method | 1 byte | 濾波器方法 |
| Interlace method | 1 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個輔助數據塊是:
數據塊摘要
關鍵數據塊、輔助數據塊和專用公共數據塊(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_SUPPORTEDtypedef 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_ptrpng_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);//灰度必須轉換成RGprintf("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數據的bufrow_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 * channelout->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的額外信息....!!!悲劇!
使用方式:
?
有待深入研究...
總結
以上是生活随笔為你收集整理的三维重建:PNG格式详解-与LibPNG使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: wps office如何打印(Offic
- 下一篇: gta5卖车的地方在哪