1. 程式人生 > 實用技巧 >TIFF檔案C語言讀取(嵌入式平臺擺脫Opencv束縛)

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 #endif
View 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