TIFF檔案C語言讀取(嵌入式平臺擺脫Opencv束縛)
一、TIFF檔案結構:
如下圖所示為TIFF影象檔案基本結構:
檔案結構主要分為三個小的描述結構Header+IFD+DE,下面就三個方面對檔案基本組成進行討論。
1.檔案頭基本組成
第1,2兩個位元組確定了檔案資料的儲存格式為大端儲存或者小端儲存,分為以下兩種情況:
- 第一位元組和第二位元組的內容組成是0x4949則為Little-endian模式。
- 第一位元組和第二位元組的內容組成是0x4D4D則為Big-endian模式。
第2,3位元組儲存了TIFF的Magic Number,所有的TIFF檔案都是42。
第4~7四個位元組用來儲存第一個IFD結構的起始位置(所有檔案相關引數都要從這裡開始查詢)。
2.IFD結構的基本組成
第一個IFD的開頭儲存了當前IFD儲存的DE的數量,在圖中為B個,每個DE的大小是12Bytes。
如果檔案的引數非常多時,就需要使用類似連結串列的方式擴充IFD結構,在IFD結構的末尾新增下一個IFD結構的Offset。
對於一般的真彩色影象而言,一般包括的DE的數量為:11個,這11個IFD檔案結構中主要包括了影象的尺寸,影象色彩深度,影象方向等資訊。
實際上IFD的功能是作為DE結構體入口的資料結構,提供了進入DE結構的門,真正的影象引數資料儲存在DE結構體當中。
3.DE結構基本組成
DE結構當中是真正儲存影象檔案相關引數的主體,結構中的每一部分代表的內容如下:
- Tag:用來表示不同的資料域,例:影象寬度(101.H)、影象高度(100.H)、影象色彩型別(106.H),要獲取DE中的內容,首先要檢查當前DE中Tag代表的資料型別,然後依次讀取資料即可。
- Types:用來確定當前DE域當中儲存資料的格式,1=BYTE、2=ASCII、3=SHORT、4=LONG、5=RATIONAL,通過確定不同的資料格式,在讀取後續資料的過程中就可以準確地將資料儲存下來。
- Count:用來記錄當前DE的影象引數資訊有多少個Types型別的資料儲存,在讀取檔案的過程中讀取即可。
- Value:用來儲存小於4Bytes的引數資料(當Count*Sizeof(Types)>4Bytes,就需要將Value當做Offset處理,從而跳轉到新的起始地址,完成引數的讀取)。
DE是用來儲存整個影象檔案引數的地方,通過從Header到IFD入口,再到每一個DE結構的入口,最後將整個影象引數提取出來,最後根據引數獲取影象Data資料。
如下所示為使用AsTiffTagViewer.exe讀取某一影象的結果:
二、相關TIFF檔案讀寫程式碼實現:
TIFF檔案C語言讀取(github地址)
有志同道合的朋友可以一起迭代一下這個TIFF的庫,可以私聊我!一起進步!
Header結構體展示:
IFD結構體展示:
DE結構體展示:
main.c
#include <stdio.h> #include <stdlib.h> #include <stdfix.h> #include <string.h> #include <math.h> #include "tiff.h" #include "image_alg.h" int main(void) { unsigned char *PATH = "C:/Users/Administrator/Desktop/mamiao/Alg_Cpp/image/A.tiff"; unsigned char *PATH_1 = "C:/Users/Administrator/Desktop/mamiao/Alg_Cpp/image/A1.bin"; unsigned char *PATH_2 = "C:/Users/Administrator/Desktop/mamiao/Alg_Cpp/image/A1.tiff"; unsigned char *Img=(unsigned char *)malloc(sizeof(unsigned char)*3000000); // printf("%d\n",mashine_endian()); Tiff_read(Img,PATH); FILE *fd; int i,j,k; fd = fopen(PATH_1,"wb+"); fseek(fd,0,SEEK_SET); // Insert the image data for(i=0;i<1000;i++){ for(j=0;j<1000;j++){ for(k=0;k<3;k++){ fwrite(Img+i*3000+j*3+k,sizeof(unsigned char),1,fd); } } } Tiff_write(Img,1000,1000,3,PATH_2); //printf("%d-%d-%d-%d\n",sizeof(long),sizeof(int),sizeof(char),sizeof(short));//4-4-1-2 printf("Finished!\n"); while(1); return 0; }View Code
tiff.c
1 #include "tiff.h" 2 #include <stdlib.h> 3 #include <string.h> 4 #include <math.h> 5 6 char mashine_endian(void) 7 { 8 int a = 1; 9 int *p = &a; 10 if(*((char *)p) == a){ 11 return 1; // little endian 12 }else{ 13 return 0; // big endian 14 } 15 } 16 17 char Tiff_read(unsigned char *Image,unsigned char *Path) 18 { 19 FILE *fd; 20 unsigned char tmp[2],buff,dev_endian = mashine_endian(),file_endian,TypeSize,Image_Start_TypeSize; 21 unsigned int DE_n=0,i=0,DE_val_size=0,channel=0,wid,hei,sum,row,clo,dep; 22 unsigned long IFD_Start=0,Image_Start; 23 size_t count; 24 IFH _IFH; 25 IFD _IFD; 26 27 fd = fopen(Path,"rb"); 28 count = fread(&(_IFH.endian),short_size,1,fd); 29 if(count != 1) { return -1;} 30 if(_IFH.endian == 0x4d4d){ 31 file_endian = 0; 32 }else{ 33 file_endian = 1; 34 } 35 count = fread(&(_IFH.magic),short_size,1,fd); 36 if(count != 1) { return -1;} 37 count = fread(&(_IFH.ifd_offset),ulong_size ,1,fd); 38 if(count != 1) { return -1;} 39 if(file_endian != dev_endian) { 40 _IFH.endian = MC_GET_SHORT(_IFH.endian); 41 _IFH.magic = MC_GET_SHORT(_IFH.magic); 42 _IFH.ifd_offset = MC_GET_LONG(_IFH.ifd_offset); 43 } 44 45 IFD_Start = _IFH.ifd_offset; 46 47 printf("Endian,Magic:%x-%x\n",_IFH.endian,_IFH.magic); 48 printf("IFD_Start Offset:%d\n",IFD_Start); 49 50 fseek(fd,IFD_Start,SEEK_SET); 51 count = fread(&(_IFD.n),short_size,1,fd); 52 if(count != 1) { return -1;} 53 if(file_endian != dev_endian) {_IFD.n = MC_GET_SHORT(_IFD.n);} 54 DE_n = _IFD.n; 55 printf("Number Of DE:%d\n",DE_n); 56 _IFD.p = (DE *)malloc(sizeof(DE)*DE_n); 57 for(i=0;i<DE_n;i++){ 58 count = fread(&((_IFD.p+i)->tag),short_size,1,fd); 59 if(count != 1) { return -1;} 60 count = fread(&((_IFD.p+i)->type),short_size,1,fd); 61 if(count != 1) { return -1;} 62 count = fread(&((_IFD.p+i)->size),ulong_size ,1,fd); 63 if(count != 1) { return -1;} 64 count = fread((&(_IFD.p+i)->val_offset),ulong_size ,1,fd); 65 if(count != 1) { return -1;} 66 if(file_endian != dev_endian) { 67 // printf("Ori_Val[Tag-Type-Size]:[%x-%x-%x]\n",(_IFD.p+i)->tag,(_IFD.p+i)->type,(_IFD.p+i)->size); 68 (_IFD.p+i)->tag = MC_GET_SHORT((_IFD.p+i)->tag); 69 (_IFD.p+i)->type = MC_GET_SHORT((_IFD.p+i)->type); 70 (_IFD.p+i)->size = MC_GET_LONG((_IFD.p+i)->size); 71 // printf("Res_Val[Tag-Type-Size]:[%x-%x-%x]\n",(_IFD.p+i)->tag,(_IFD.p+i)->type,(_IFD.p+i)->size); 72 switch((_IFD.p+i)->type){ 73 case 1: TypeSize = 1; 74 case 2: DE_val_size = (_IFD.p+i)->size; break; 75 case 3: TypeSize = 2; DE_val_size = (_IFD.p+i)->size * TypeSize; break; 76 case 4: TypeSize = 4; DE_val_size = (_IFD.p+i)->size * TypeSize; break; 77 case 5: TypeSize = 8; DE_val_size = (_IFD.p+i)->size * TypeSize; break; 78 default: TypeSize = 1; break; 79 } 80 if(DE_val_size == 1){ 81 (_IFD.p+i)->val_offset = MC_GET_CHAR((_IFD.p+i)->val_offset); 82 }else if(DE_val_size == 2){ 83 (_IFD.p+i)->val_offset = MC_GET_SHORT((_IFD.p+i)->val_offset); 84 }else if(DE_val_size >= 4){ 85 (_IFD.p+i)->val_offset = MC_GET_LONG((_IFD.p+i)->val_offset); 86 } 87 } 88 // printf("Tag Value:%d\n",(_IFD.p+i)->tag); 89 if(256 == (_IFD.p+i)->tag){ // 影象寬度儲存位置 90 wid = (_IFD.p+i)->val_offset; 91 printf("DE-Width of Img-[Tag:%d Type:%d Size:%d Offset:%d]\n",(_IFD.p+i)->tag,(_IFD.p+i)->type,(_IFD.p+i)->size,(_IFD.p+i)->val_offset); 92 continue; 93 } 94 if(257 == (_IFD.p+i)->tag){ // 影象高度儲存位置 95 hei = (_IFD.p+i)->val_offset; 96 printf("DE-Height of Img-[Tag:%d Type:%d Size:%d Offset:%d]\n",(_IFD.p+i)->tag,(_IFD.p+i)->type,(_IFD.p+i)->size,(_IFD.p+i)->val_offset); 97 continue; 98 } 99 if(258 == (_IFD.p+i)->tag){ // 影象每個通道灰度等級 eg:RGB[8,8,8] 100 printf("DE-BitsPerSample of Img-[Tag:%d Type:%d Size:%d Offset:%d]\n",(_IFD.p+i)->tag,(_IFD.p+i)->type,(_IFD.p+i)->size,(_IFD.p+i)->val_offset); 101 continue; 102 } 103 if(259 == (_IFD.p+i)->tag){ // 影象採用的壓縮演算法 1-NoCompression 2-CCITT ... 104 printf("DE-Compression of Img-[Tag:%d Type:%d Size:%d Offset:%d]\n",(_IFD.p+i)->tag,(_IFD.p+i)->type,(_IFD.p+i)->size,(_IFD.p+i)->val_offset); 105 continue; 106 } 107 if(262 == (_IFD.p+i)->tag){ // 表示影象的種類: 0-WhiteisZero 1-BlackisZero 2-RGBImg 3-PaletteColor ... 108 if((_IFD.p+i)->val_offset == 2){ 109 channel = 3; 110 }else{ 111 channel = 1; 112 } 113 printf("DE-PhotometricInyerpretation of Img-[Tag:%d Type:%d Size:%d Offset:%d]\n",(_IFD.p+i)->tag,(_IFD.p+i)->type,(_IFD.p+i)->size,(_IFD.p+i)->val_offset); 114 continue; 115 } 116 if(273 == (_IFD.p+i)->tag){ // 影象資料起始地址儲存位置相對於檔案開始的位置val的儲存位置 117 Image_Start = (_IFD.p+i)->val_offset; 118 Image_Start_TypeSize = TypeSize; 119 printf("DE-StripOffsets of Img-[Tag:%d Type:%d Size:%d Offset:%d]\n",(_IFD.p+i)->tag,(_IFD.p+i)->type,(_IFD.p+i)->size,(_IFD.p+i)->val_offset); 120 continue; 121 } 122 if(274 == (_IFD.p+i)->tag){ // 影象座標系方式: 1[left top] 2[right top] 3[bottom right] ... 123 printf("DE-Orientation of Img-[Tag:%d Type:%d Size:%d Offset:%d]\n",(_IFD.p+i)->tag,(_IFD.p+i)->type,(_IFD.p+i)->size,(_IFD.p+i)->val_offset); 124 continue; 125 } 126 if(277 == (_IFD.p+i)->tag){ // 表示影象的格式為3[RGB]或1[bilevel,grayscale,palette-color] image 127 printf("DE-SamplePerPixel of Img-[Tag:%d Type:%d Size:%d Offset:%d]\n",(_IFD.p+i)->tag,(_IFD.p+i)->type,(_IFD.p+i)->size,(_IFD.p+i)->val_offset); 128 continue; 129 } 130 if(278 == (_IFD.p+i)->tag){ // 表示每一個Strip內包含的影象的行數 eg:Img[w][h][w] --> RowsPerStrip=1 131 printf("DE-RowsPerStrip of Img-[Tag:%d Type:%d Size:%d Offset:%d]\n",(_IFD.p+i)->tag,(_IFD.p+i)->type,(_IFD.p+i)->size,(_IFD.p+i)->val_offset); 132 continue; 133 } 134 if(279 == (_IFD.p+i)->tag){ // 表示每一個Strip的Bytes大小 eg:Img[w][h][d] --> StripByteCounts=w*d 135 printf("DE-StripByteCounts of Img-[Tag:%d Type:%d Size:%d Offset:%d]\n",(_IFD.p+i)->tag,(_IFD.p+i)->type,(_IFD.p+i)->size,(_IFD.p+i)->val_offset); 136 continue; 137 } 138 if(284 == (_IFD.p+i)->tag){ // 全綵影象每一個畫素點排列方式: 1[RGB...RGB]-交叉 2[[R...R],[G...G],[B...B]]-平鋪 139 printf("DE-PlanarConfiguration of Img-[Tag:%d Type:%d Size:%d Offset:%d]\n",(_IFD.p+i)->tag,(_IFD.p+i)->type,(_IFD.p+i)->size,(_IFD.p+i)->val_offset); 140 continue; 141 } 142 } 143 144 fseek(fd,Image_Start,SEEK_SET); 145 fread(&Image_Start,Image_Start_TypeSize,1,fd); // Image_Start:影象資料起始地址儲存位置相對於檔案開始的位置 146 if(Image_Start_TypeSize == 2){ 147 Image_Start = MC_GET_SHORT(Image_Start); 148 }else if(Image_Start_TypeSize == 4){ 149 Image_Start = MC_GET_LONG(Image_Start); 150 } 151 printf("The image data start address:%d\n",Image_Start); 152 fseek(fd,Image_Start,SEEK_SET); 153 for(row=0;row<hei;row++){ 154 for(clo=0;clo<wid;clo++){ 155 for(dep=0;dep<3;dep++){ 156 count = fread(&buff,sizeof(unsigned char),1,fd); 157 if(count != 1) { return -1;} 158 *(Image + row*wid*channel + clo*channel + dep) = buff; 159 } 160 } 161 } 162 free(_IFD.p); 163 fclose(fd); 164 return 0; 165 } 166 167 char Tiff_write(unsigned char const *Image,unsigned int w,unsigned int h,unsigned char channel, unsigned char *Path) 168 { 169 IFH _IFH; 170 IFD _IFD; 171 FILE * fd; 172 fd = fopen(Path,"wb+"); 173 unsigned long block_size = w*channel, sum = h*block_size; 174 unsigned long i,j,k; 175 // Offset=w*h*d + 8(eg:Img[1000][1000][3] --> 3000008) 176 // RGB Full Color:W H BitsPerSample Compression Photometric StripOffset Orientation SamplePerPixle RowsPerStrip StripByteCounts PlanarConfiguration 177 short DE_tag[DE_N]={256,257,258,259,262,273,274,277,278,279,284}; 178 short DE_type[DE_N]={3, 3, 3, 3, 3, 4, 3, 3, 3, 4, 3}; 179 unsigned long DE_size[DE_N]={1,1,3,1,1,h,1,1,1,h,1}; 180 unsigned long DE_val_offset[DE_N]={w,h,sum+146,1,2,sum+4152,1,3,1,sum+152,1}; 181 short RGB_Scale[3] = {8,8,8}; 182 unsigned long StripOffset = 8; 183 184 185 _IFH.endian = 0x4949; 186 _IFH.magic = 42; 187 _IFH.ifd_offset = sum + 8; 188 189 _IFD.n = DE_N; 190 _IFD.p = (DE *)malloc(sizeof(DE)*_IFD.n); 191 _IFD.next_ifd_offset = 0; 192 for(i=0;i<DE_N;i++){ 193 (_IFD.p+i)->tag = DE_tag[i]; 194 (_IFD.p+i)->type = DE_type[i]; 195 (_IFD.p+i)->size = DE_size[i]; 196 (_IFD.p+i)->val_offset = DE_val_offset[i]; 197 } 198 199 fseek(fd,0,SEEK_SET); 200 fwrite(&(_IFH.endian),short_size,1,fd); 201 fwrite(&(_IFH.magic),short_size,1,fd); 202 fwrite(&(_IFH.ifd_offset),ulong_size,1,fd); 203 204 fseek(fd,_IFH.ifd_offset,SEEK_SET); 205 fwrite(&(_IFD.n),short_size,1,fd); 206 for(i=0;i<_IFD.n;i++){ 207 fwrite(&((_IFD.p+i)->tag),short_size,1,fd); 208 fwrite(&((_IFD.p+i)->type),short_size,1,fd); 209 fwrite(&((_IFD.p+i)->size),ulong_size,1,fd); 210 fwrite(&((_IFD.p+i)->val_offset),ulong_size,1,fd); 211 } 212 fwrite(&(_IFD.next_ifd_offset),ulong_size,1,fd); 213 214 printf("%d-%d\n",(_IFD.p+2)->val_offset,(_IFD.p+2)->size); // 3000146-3 215 fseek(fd,(_IFD.p+2)->val_offset,SEEK_SET); // Setup the RGB grayscale for RGB image[8,8,8] 216 for(i=0;i<(_IFD.p+2)->size;i++){ 217 fwrite(RGB_Scale+i,short_size,1,fd); 218 } 219 220 fseek(fd,(_IFD.p+5)->val_offset,SEEK_SET); 221 printf("%d-%d\n",(_IFD.p+5)->val_offset,(_IFD.p+5)->size); // 3004152-1000 222 for(i=0;i<(_IFD.p+5)->size;i++){ 223 fwrite(&StripOffset,ulong_size,1,fd); // For Small TIFF Need to change the data-type to short. 224 // printf("%d-%d-%d-%d\n",StripOffset,block_size,(_IFD.p+5)->size,i); 225 StripOffset += block_size; 226 } 227 228 printf("%d-%d\n",(_IFD.p+9)->val_offset,(_IFD.p+9)->size); 229 fseek(fd,(_IFD.p+9)->val_offset,SEEK_SET); // Insert the block_size for every StripOffset 230 for(i=0;i<(_IFD.p+9)->size;i++){ 231 fwrite(&block_size,ulong_size,1,fd); 232 } 233 fseek(fd,8,SEEK_SET); // Insert the image data 234 for(i=0;i<h;i++){ 235 for(j=0;j<w;j++){ 236 for(k=0;k<channel;k++){ 237 fwrite(Image+i*block_size+j*channel+k,sizeof(unsigned char),1,fd); 238 } 239 } 240 } 241 }View Code
tiff.h
1 #ifndef _TIFF_H_ 2 #define _TIFF_H_ 3 #include <stdio.h> 4 5 #define MC_GET_CHAR(__data__) __data__ 6 #define MC_GET_SHORT(__data__) ((__data__ & 0xFF) << 8) | (__data__ >> 8) 7 #define MC_GET_LONG(__data__) ((__data__ & 0xFF) << 24) | ((__data__ & 0xFF00) << 8) | ((__data__ & 0xFF0000) >> 8) | ((__data__ & 0xFF000000) >> 24) 8 9 #define DE_N 11 10 #define short_size sizeof(short) 11 #define ulong_size sizeof(unsigned long) 12 13 typedef struct 14 { 15 short endian; // 位元組順序標誌位,值為II或者MM:II表示小位元組在前,又稱為little-endian,MM表示大位元組在前,又稱為big-endian 16 short magic; // TIFF的標誌位,一般都是42 17 unsigned long ifd_offset; // 第一個IFD的偏移量,可以在任意位置,但必須是在一個字的邊界,也就是說必須是2的整數倍 18 }IFH; 19 20 typedef struct 21 { 22 short tag; // 此TAG的唯一標識 23 short type; // 資料型別 24 unsigned long size; // 數量,通過型別和數量可以確定儲存此TAG的資料需要佔據的位元組數 25 unsigned long val_offset; 26 }DE; 27 28 typedef struct 29 { 30 short n; // 表示此IFD包含了多少個DE,假設數目為n 31 DE *p; // n個DE 32 unsigned long next_ifd_offset; // 下一個IFD的偏移量,如果沒有則置為0 33 }IFD; 34 35 char mashine_endian(void); 36 char Tiff_read(unsigned char *Image,unsigned char *Path); // 以平鋪platted方式來排布RGB資料,每一個畫素點對應三個連續的資料(RGB) 37 char Tiff_write(unsigned char const *Image,unsigned int w,unsigned int h,unsigned char channel, unsigned char *Path); 38 #endifView Code
三、JPEG/PNG到TIFF檔案轉換以及TIFF讀寫函式驗證:
1.Matlab驗證TIFF讀取正確性
1 %% 2 % C:\Users\Administrator\Desktop\mamiao\Alg_Cpp\image 3 I = imread('C:/Users/Administrator/Desktop/mamiao/Alg_Cpp/image/A.tiff'); 4 f = fopen('C:/Users/Administrator/Desktop/mamiao/Alg_Cpp/image/A1.bin','r'); 5 [height, width, depth] = size(I); 6 for i = 1 : width 7 for j = 1 : height 8 for k = 1 : depth 9 tmp = fread(f,1); 10 if (I(i,j,k) - tmp) ~= 0 11 i,j,k 12 end 13 end 14 end 15 end 16 fclose(f);
2.Matlab轉換影象格式:
1 I=imread('C:\Users\Administrator\Desktop\ImgB.'); 2 imwrite(I,'C:\Users\Administrator\Desktop\CImgB.tiff','Compression','none');
Reference:
下載已經整理好的資源:TIFF白皮書&Code&Software
TIFF影象檔案格式詳解:https://www.cnblogs.com/WangGuiHandsome/p/10024243.html