TIFFファイルにはJPEGファイルと同様にEXIF情報を含めることができます。同様にGPS情報も持つことができます。
これらのタグを説明する前に、まず筆者自身のおさらいとして、TIFFのタグ、ディレクトリ構造を説明します。
IFDとタグ
TIFFファイルは1つのファイル内に複数の画像を持つことができますが、
その各画像セットをIFD(Image File Directory)と呼びます。
IFDはタグと画像データからなっているので、同一ファイル内にフォーマット(色数やビット深度等)が異なる画像を複数持つことも可能です。
タグは12byte固定長で記録されています。
タグは、タグID、データ型、データ数、データまたはデータへのオフセットからなります。
データは4byte以下ならタグフィールド内に、それ以上であればファイルの何処かに記録され、タグにはそのデータまでのオフセットが記録されます。
格納されるデータはタグによって決まっており、例えばImageLengthタグでは4byte符号なし整数が格納されることとなっています。
libtiffにおけるIFD、タグの取り扱い
libtiffを使用する場合、タグのデータを取得する関数はTIFFGetField()
です。
この関数は引数にタグIDとデータを格納する変数を指定します。場合によってはデータカウントも指定します。
つまり、プログラマ側はIFDを先頭からなぞっていく必要はなく、欲しいタグを直接指定してデータを取り出すことが出来るようになっています。
ただし、上記のとおりタグに格納されるデータはタグによって決まっているので、
プログラマ側はどのタグにどのような情報がいくつ入っているかを正しく知っておく必要があります。
間違って指定すると赤バツが出たりします。
タグにどのような情報が入っているかはtif_dirinfo.c内にTIFFField
型の配列という形で定義されています。
TIFFField
型の定義は以下のとおりです。
1: 2: struct _TIFFField { 3: uint32 field_tag; /* field's tag */ 4: short field_readcount; /* read count/TIFF_VARIABLE/TIFF_SPP */ 5: short field_writecount; /* write count/TIFF_VARIABLE */ 6: TIFFDataType field_type; /* type of associated data */ 7: uint32 reserved; /* reserved for future extension */ 8: TIFFSetGetFieldType set_field_type; /* type to be passed to TIFFSetField */ 9: TIFFSetGetFieldType get_field_type; /* type to be passed to TIFFGetField */ 10: unsigned short field_bit; /* bit in fieldsset bit vector */ 11: unsigned char field_oktochange; /* if true, can change while writing */ 12: unsigned char field_passcount; /* if true, pass dir count on set */ 13: char* field_name; /* ASCII name */ 14: TIFFFieldArray* field_subfields; /* if field points to child ifds, child ifd field definition array */ 15: };
field_readcount
、field_writecount
は、カウント数が決まっている場合の読み込み、書き込みのカウント数です。
ここに-1(TIFF_VARIABLE
)または-3(TIFF_VARIABLE2
)の場合は可変長であることを示します。
読み込みと書き込みで値が違う場合があるようですが、これはよくわかりません。
field_type
はデータの型です。
この後のset_field_type
、get_field_type
と関連してはいますが、同じとは限らないようです。
これもいまいちよくわかっていません。
field_passcount
は、1ならばTIFFGetField()
およびTIFFSetField()
関数で第3引数にカウント数を与える必要があります。
通常タグとEXIFタグではTIFFField
型の配列がtif_dirinfo.cで定義されているのでプログラマ側はあまり意識する必要はありませんが、
GPSタグは定義されていないのでユーザ側がTIFFField
型の配列を定義する必要があります。
また、EXIFタグの定義はv4.0以降からのようです。
それ以前のバージョンの場合はGPSタグと同様に自前でフィールド定義を作成する必要があります。
EXIF、GPSのIFD
EXIF、GPS情報は単一ではなく多数の情報からなっています。
したがって、それらを他のタグと同様にずらずらと並べると取り扱いが煩雑になってしまいそうです。
フォーマットの策定者がそのように考えたかどうかはわかりませんが、EXIF、GPSタグはそれぞれ別のIFDとして格納され、
先頭のIFDには、EXIFやGPSのIFDまでのオフセットがExifIfd
、GpsIfd
タグにそれぞれ記録されます。
EXIF、GPSのタグを読み込むには、上記のoffsetを取得してEXIFやGPSのIFDを読み込んでからEXIF、GPSのタグを取得していきます。
タグの取得は通常のタグと同様にTIFFGetField()
関数を使用します。
ただし、GPSタグはlibtiff内に定義されていないので、自分でTIFFField
型の配列を定義する必要があります。
EXIFタグをすべて取得するには
ところで、TIFFGetField()
はタグを指定して取得する形なので、
ファイル内にある全てのタグを取得したいといった場合には意外と不便だったりします。
以下の例はhttp://docs.thefoundry.co.uk/nuke/63/ndkdevguide/examples/tiffReader.cpp
の例をもとに少し修正したものです。
取得したデータを保持しておく箇所は省いているので注意してください。
タグによって取得される型が異なるので、なにかVariant型のようなものを用意できると便利でしょう。
1: // 取得したデータは何かVariant型のようなものが用意出来ればそれに詰め込むと良いでしょう 2: // 同時にカウント数も保持しておく必要がある 3: 4: // 5: // 文字列、Undefined以外のデータの場合 6: // 7: template<class T, class V> 8: static bool getMetaData( TIFF* metatif, const TIFFField* fi ) 9: { 10: if (fi->field_readcount == TIFF_VARIABLE2 || fi->field_readcount == TIFF_VARIABLE || 11: fi->field_readcount > 1) 12: { 13: size_t count = 0; 14: T* data; 15: 16: if (fi->field_readcount == TIFF_VARIABLE) 17: { 18: uint16 gotcount = 0; 19: TIFFGetField(metatif, fi->field_tag, &gotcount, &data); 20: count = gotcount; 21: } 22: else if (fi->field_readcount == TIFF_VARIABLE2) 23: { 24: uint32 gotcount = 0; 25: TIFFGetField(metatif, fi->field_tag, &gotcount, &data); 26: count = gotcount; 27: } 28: else 29: { 30: TIFFGetField(metatif, fi->field_tag, &data); 31: count = fi->field_readcount; 32: } 33: 34: info.nCount = count; 35: 36: for (unsigned i = 0; i < count; i++) 37: { 38: // データを何処かに記録 39: // カウントも記録しておく必要がある 40: } 41: 42: return true; 43: } 44: else if (fi->field_readcount == 1) 45: { 46: T data; 47: TIFFGetField(metatif, fi->field_tag, &data); 48: 49: // データを何処かに記録 50: // ここでもカウント1を記録しておく必要がある 51: 52: return true; 53: } 54: 55: return false; 56: } 57: 58: 59: // 60: // 文字列データの場合 61: // 62: static bool getMetaDataString(TIFF* metatif, const TIFFField* fi ) 63: { 64: if (fi->field_readcount > 1) 65: { 66: char* data; 67: TIFFGetField(metatif, fi->field_tag, &data); 68: // カウント1を記録しておく必要がある 69: return true; 70: } 71: 72: return false; 73: } 74: 75: 76: // 77: // Undefinedがとんできた場合 78: // 79: static bool getMetadataUndefined( TIFF* metatif, const TIFFField* fi ) 80: { 81: size_t count = 0; 82: uint8* data; 83: 84: if (fi->field_readcount == TIFF_VARIABLE2 || fi->field_readcount == TIFF_VARIABLE || 85: fi->field_readcount > 1) 86: { 87: if (fi->field_readcount == TIFF_VARIABLE) 88: { 89: uint16 gotcount = 0; 90: TIFFGetField(metatif, fi->field_tag, &gotcount, &data); 91: count = gotcount; 92: } 93: else if (fi->field_readcount == TIFF_VARIABLE2) 94: { 95: uint32 gotcount = 0; 96: TIFFGetField(metatif, fi->field_tag, &gotcount, &data); 97: count = gotcount; 98: } 99: else 100: { 101: TIFFGetField(metatif, fi->field_tag, &data); 102: count = fi->field_readcount; 103: } 104: 105: // ここでカウントとデータを記録する 106: 107: return true; 108: } 109: else if (fi->field_readcount == 1) 110: { 111: TIFFGetField(metatif, fi->field_tag, &data); 112: 113: // ここでカウント1とデータを記録する 114: 115: return true; 116: } 117: return false; 118: } 119: 120: 121: bool GetExif( const char *pszFName ) 122: { 123: TIFF *pTif = TIFFOpen( pszFName, "r" ); 124: if ( !pTif ) return false; 125: 126: uint64 nReadDirOffset = 0; 127: int bRet; 128: 129: bRet = TIFFGetField( pTif, TIFFTAG_EXIFIFD, &nReadDirOffset ); 130: 131: if ( bRet ) 132: { 133: TIFFReadEXIFDirectory( pTif, nReadDirOffset ); 134: int nCnt = TIFFGetTagListCount( pTif ); 135: 136: for ( int i = 0; i < nCnt; i++ ) 137: { 138: ttag_t tag = TIFFGetTagListEntry( pTif, i ); 139: const TIFFField *pFieldInfo = TIFFFieldWithTag( pTif, tag ); 140: 141: // 戻り値 142: bool usedMetaData = false; 143: 144: switch ( pFieldInfo->field_type ) 145: { 146: case TIFF_RATIONAL: 147: case TIFF_SRATIONAL: 148: case TIFF_FLOAT: 149: usedMetaData = getMetaData<float, double>( pTif, pFieldInfo ); 150: break; 151: 152: case TIFF_SHORT: 153: usedMetaData = getMetaData<uint16, int>( pTif, pFieldInfo ); 154: break; 155: 156: case TIFF_LONG: 157: usedMetaData = getMetaData<uint32, double>( pTif, pFieldInfo ); 158: break; 159: 160: case TIFF_SBYTE: 161: usedMetaData = getMetaData<int8, int>( pTif, pFieldInfo ); 162: break; 163: 164: case TIFF_SSHORT: 165: usedMetaData = getMetaData<int16, int>( pTif, pFieldInfo ); 166: break; 167: 168: case TIFF_SLONG: 169: usedMetaData = getMetaData<int32, int>( pTif, pFieldInfo ); 170: break; 171: 172: case TIFF_DOUBLE: 173: usedMetaData = getMetaData<double, double>( pTif, pFieldInfo ); 174: break; 175: 176: case TIFF_ASCII: 177: usedMetaData = getMetaDataString( pTif, pFieldInfo ); 178: break; 179: 180: case TIFF_UNDEFINED: 181: usedMetaData = getMetadataUndefined( pTif, pFieldInfo ); 182: break; 183: 184: default: 185: 186: break; 187: } 188: 189: if ( usedMetaData ) 190: { 191: // 本来は各関数の引数でデータを受け取って、 192: // 戻り値で保存するかどうか判断するべき? 193: } 194: } 195: } 196: 197: TIFFClose( pTif ); 198: 199: return true; 200: }
EXIFのMakerNoteタグと、MakeタグとModelタグ
EXIFにはMakerNoteタグというのがあります。
これはメーカー独自の情報を記録するタグで、中身のフォーマットもそれぞれのメーカー定義のもののようです。
ExifToolなどはMakeタグとModelタグを見て中身を解析してくれたりします。
なので、対応するMakeタグとModelタグも、あれば読み込んでおいて、保存時には書き込んでおきましょう。
GPSタグ
EXIFタグはlibtiff内に定義されていましたが、GPSタグは定義されていません。
そこで、タグ情報のTIFFField
型の配列を自前で作成する必要があります。
以下は筆者が作成したものですが、以下のタグは確認できていますが他は未確認です。
- GPSLatitudeRef
- GPSLongituideRef
- GPSTimeStamp
- GPSStatus
- GPSMapDatum
- GPSDateStamp
- GPSDateTime
- GPSLatitude
- GPSLongitude
- GPSPosition
1: // 2: // タグ番号 3: // 下記のサイトの情報を元にGPSタグのものと合わせています。 4: // http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/gps.html 5: // 6: // 7: #define GPSTAG_VERSIONID 0 8: #define GPSTAG_LATITUDEREF 1 9: #define GPSTAG_LATITUDE 2 10: #define GPSTAG_LONGITUDEREF 3 11: #define GPSTAG_LONGITUDE 4 12: #define GPSTAG_ALTITUDEREF 5 13: #define GPSTAG_ALTITUDE 6 14: #define GPSTAG_TIMESTAMP 7 15: #define GPSTAG_SATELLITES 8 16: #define GPSTAG_STATUS 9 17: #define GPSTAG_MEASUREMODE 10 18: #define GPSTAG_DOP 11 19: #define GPSTAG_SPEEDREF 12 20: #define GPSTAG_SPEED 13 21: #define GPSTAG_TRACKREF 14 22: #define GPSTAG_TRACK 15 23: #define GPSTAG_IMGDIRECTIONREF 16 24: #define GPSTAG_IMGDIRECTION 17 25: #define GPSTAG_MAPDATUM 18 26: #define GPSTAG_DESTLATITUDEREF 19 27: #define GPSTAG_DESTLATITUDE 20 28: #define GPSTAG_DESTLONGITUDEREF 21 29: #define GPSTAG_DESTLONGITUDE 22 30: #define GPSTAG_DESTBEARINGREF 23 31: #define GPSTAG_DESTBEARING 24 32: #define GPSTAG_DESTDISTANCEREF 25 33: #define GPSTAG_DESTDISTANCE 26 34: #define GPSTAG_PROCESSINGMETHOD 27 35: #define GPSTAG_AREAINFORMATION 28 36: #define GPSTAG_DATASTAMP 29 37: #define GPSTAG_DIFFERENTIAL 30 38: 39: // 40: // フィールド情報定義 41: // ここがキモ 42: // 43: static TIFFField gpsFields[] = { 44: { GPSTAG_VERSIONID, 4, 4, TIFF_BYTE, 0, TIFF_SETGET_UINT8, TIFF_SETGET_UINT8, FIELD_CUSTOM, 1, 0, "GPSVersionID", NULL }, 45: { GPSTAG_LATITUDEREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_ASCII, FIELD_CUSTOM, 1, 0, "GPSLatitudeRef", NULL }, 46: { GPSTAG_LATITUDE, -1, -1, TIFF_RATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_C16_FLOAT, FIELD_CUSTOM, 1, 1, "GPSLatitude", NULL }, 47: { GPSTAG_LONGITUDEREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_ASCII, FIELD_CUSTOM, 1, 0, "GPSLongitudeRef", NULL }, 48: { GPSTAG_LONGITUDE, -1, -1, TIFF_RATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_C16_FLOAT, FIELD_CUSTOM, 1, 1, "GPSLongitude", NULL }, 49: { GPSTAG_ALTITUDEREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_ASCII, FIELD_CUSTOM, 1, 0, "GPSAltitudeRef", NULL }, 50: { GPSTAG_ALTITUDE, -1, -1, TIFF_RATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_C16_FLOAT, FIELD_CUSTOM, 1, 1, "GPSAltitude", NULL }, 51: { GPSTAG_TIMESTAMP, -1, -1, TIFF_RATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_C16_FLOAT, FIELD_CUSTOM, 1, 1, "GPSTimeStamp", NULL }, 52: { GPSTAG_SATELLITES, -1, -1, TIFF_SHORT, 0, TIFF_SETGET_C16_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "GPSSatellites", NULL }, 53: { GPSTAG_STATUS, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "GPSStatus", NULL }, 54: { GPSTAG_MEASUREMODE, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "GPSMeasureMode", NULL }, 55: { GPSTAG_DOP, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "GPSDOP", NULL }, 56: { GPSTAG_SPEEDREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "GPSSpeedRef", NULL }, 57: { GPSTAG_SPEED, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "GPSSpeed", NULL }, 58: { GPSTAG_TRACKREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "GPSTrackRef", NULL }, 59: { GPSTAG_TRACK, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "GPSTrack", NULL }, 60: { GPSTAG_IMGDIRECTIONREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "GPSImgDirectionRef", NULL }, 61: { GPSTAG_IMGDIRECTION, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "GPSImgDirection", NULL }, 62: { GPSTAG_MAPDATUM, -1, -1, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "GPSMapDatum", NULL }, 63: { GPSTAG_DESTLATITUDEREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "GPSDestLatitudeRef", NULL }, 64: { GPSTAG_DESTLATITUDE, -1, -1, TIFF_RATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_C16_FLOAT, FIELD_CUSTOM, 1, 1, "GPSDestLatitude", NULL }, 65: { GPSTAG_DESTLONGITUDEREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "GPSDestLongitudeRef", NULL }, 66: { GPSTAG_DESTLONGITUDE, -1, -1, TIFF_RATIONAL, 0, TIFF_SETGET_C16_FLOAT, TIFF_SETGET_C16_FLOAT, FIELD_CUSTOM, 1, 1, "GPSDestLongitude", NULL }, 67: { GPSTAG_DESTBEARINGREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "GPSDestBearingRef", NULL }, 68: { GPSTAG_DESTBEARING, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "GPSDestBearing", NULL }, 69: { GPSTAG_DESTDISTANCEREF, 2, 2, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "GPSDestDistanceRef", NULL }, 70: { GPSTAG_DESTDISTANCE, 1, 1, TIFF_RATIONAL, 0, TIFF_SETGET_DOUBLE, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "GPSDestDistance", NULL }, 71: { GPSTAG_PROCESSINGMETHOD, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "GPSProcessingMethod", NULL }, 72: { GPSTAG_AREAINFORMATION, -1, -1, TIFF_UNDEFINED, 0, TIFF_SETGET_C16_UINT8, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 1, "GPSAreaInformation", NULL }, 73: { GPSTAG_DATASTAMP, 11, 11, TIFF_ASCII, 0, TIFF_SETGET_ASCII, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "GPSDateStamp", NULL }, 74: { GPSTAG_DIFFERENTIAL, 1, 1, TIFF_SHORT, 0, TIFF_SETGET_UINT16, TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "GPSDifferential", NULL } 75: }; 76: 77: // 78: // この変数を用意して、TIFFReadCustomDirectory()に渡す 79: // 80: static TIFFFieldArray gpsFieldArray = { tfiatOther, 0, 31, gpsFields }; 81: 82: ///////////////////////////////////////////////////////////////////// 83: 84: bool GetGps( const char *pszFName ) 85: { 86: // GPS IFD 87: nReadDirOffset = 0; 88: TIFFSetDirectory( pTif, 0 ); 89: 90: // GPS IFDまでのオフセットを取得 91: bRet = TIFFGetField( pTif, TIFFTAG_GPSIFD, &nReadDirOffset ); 92: 93: if ( bRet ) 94: { 95: // カスタムディレクトリとしてGPS IFDを読み込む 96: // ここで、上の変数を渡す 97: TIFFReadCustomDirectory( pTif, nReadDirOffset, &gpsFieldArray ); 98: int nCnt = TIFFGetTagListCount( pTif ); 99: 100: for ( int i = 0; i < nCnt; i++ ) 101: { 102: ttag_t tag = TIFFGetTagListEntry( pTif, i ); 103: const TIFFField *pFieldInfo = TIFFFieldWithTag( pTif, tag ); 104: 105: // 以下はEXIFタグと同じ 106:
書き込み
EXIF、GPSタグの書き込みは、基本的には新しくIFDを作成してTIFFSetField()
でタグを書き込み、
最後にTIFFWriteCustomDirectory()
でIFDを書き込むという手順です。
ここでは書き込みの概略を示します。
1: uint64 nExifDirOffset, nGpsDirOffset; 2: 3: // EXIF 4: 5: if ( TIFFCreateEXIFDirectory( pTif ) != 0 ) 6: { 7: return false; 8: } 9: 10: // EXIFタグを書き込む例 11: TIFFSetField( pTif, EXIFTAG_ISOSPEEDRATINGS, 200 ) 12: 13: // EXIF IFDを書き込む 14: if ( !TIFFWriteCustomDirectory( pTif, &nExifDirOffset ) ) 15: { 16: return false; 17: } 18: 19: // GPS 20: // 変数gpsFieldArrayは上記参照 21: 22: if ( TIFFCreateCustomDirectory( pTif, &gpsFieldArray ) ) 23: { 24: return false; 25: } 26: 27: // GPSタグを書き込む例 28: // タグ定義は上記参照 29: TIFFSetField( pTif, GPSTAG_LATITUDEREF, "N" ); 30: 31: // GPS IFDを書き込む 32: if ( !TIFFWriteCustomDirectory( pTif, &nGpsDirOffset ) ) 33: { 34: return false; 35: } 36: 37: // 先頭のIFDにEXIFIFD、GPSIFDへのオフセットタグを書き込む 38: TIFFSetDirectory( pTif, 0 ); 39: TIFFSetField( pTif, TIFFTAG_EXIFIFD, nExifDirOffset ); 40: TIFFSetField( pTif, TIFFTAG_GPSIFD, nGpsDirOffset ); 41: