TIFFメタデータ・フォーマットの仕様と使用上の注意
イメージを読む 「イメージを書く」「ネイティブ・ストリーム・メタデータ形式」
「ネイティブ・イメージ・メタデータ形式」
イメージを読む
TIFFイメージはImageReaderによって読み取られます。ImageReaderは、公開されたTIFFImageReadParamを介して公開されているインタフェースと同様に制御されます。色変換
ソース・イメージ・データが測光型CIE L*a*b*またはYCbCrを有し、かつ宛先色空間型がRGBである場合、ソース・イメージ・データは、内部カラー変換器を使用して自動的にRGBに変換されます。
カラー・スペース
デフォルトで割り当てられた未加工の色空間、つまりユーザーが提供するImageTypeSpecifierがない場合、次のうち最初に適用される色空間が適用されます:- 「ICCプロファイル」メタデータ・フィールドが存在し、イメージ・データ・レイアウトと互換性がある場合は、「ICCプロファイル」メタデータ・フィールドから作成された色空間。
- イメージがモノクロ/バイ・レベルの場合はsRGB (内部的に2レベルのカラー・マップが作成されます)。
- イメージがパレット・カラーの場合はsRGB。
- イメージがピクセルあたり3つのサンプルを持ち、測光型CIE L*a*b*を持つか、測光型YCbCrを持ち、JPEG圧縮されていない場合、リニアRGB。
- イメージが測光型のCMYKと1ピクセルあたり4つのサンプルを持つ場合は「デフォルトのCMYKカラー・スペース」。
- イメージがピクセルあたり1つまたは2つのサンプルを持ち、1サンプルあたり1、2、4、8、16、または32ビットが均一であるか、浮動小数点の場合はグレースケール。
- イメージが1ピクセルあたり3つまたは4つのサンプルを持ち、1サンプルあたり1、2、4、8、16、または32ビットが均一であるか浮動小数点である場合はsRGB。
- イメージがピクセルあたり4つ以上のサンプルを持ち、すべてのバンドのサンプルあたりのビット数が同じで、8の倍数である場合は、作成された「ジェネリック・カラー・スペース」です。
- イメージが1サンプルあたりのビット数に関係なく、1ピクセルあたり1つまたは2つのサンプルを有する場合、グレースケール。
- イメージが1サンプルあたりのビット数に関係なく、ピクセルあたり3つまたは4つのサンプルを持つ場合、sRGB。
- 1つのサンプルあたりのビット数に関係なく、イメージが1ピクセルあたり4つ以上のサンプルを持つ場合は作成された「ジェネリック・カラー・スペース」です。
デフォルトのCMYK色空間に使用される正規化色座標変換は、次のように定義されます:
- リニアRGBへのCMYK
R = (1 - K)*(1 - C) G = (1 - K)*(1 - M) B = (1 - K)*(1 - Y)
- リニアRGBからCMYKへ
K = min{1 - R, 1 - G, 1 - B} if(K != 1) { C = (1 - R - K)/(1 - K) M = (1 - G - K)/(1 - K) Y = (1 - B - K)/(1 - K) } else { C = M = Y = 0 }
他の色空間が推測されない場合に使用される一般的な色空間は、単にデータが読み込まれるようにするために提供されます。 あらゆる種類の正確な変換を提供するためのものではありません。
データが前述のものによって正しく処理されない色空間にあることが分かっている場合は、ImageTypeSpecifier
をリーダーに供給し、問題のデータに適した色空間から導出する必要があります。
ICCプロファイル
ICCプロファイルがイメージ・メタデータ(BaselineTIFFTagSet.TAG_ICC_PROFILE、タグ番号34675)に含まれている場合、それを使用してロードされたイメージのカラー・スペースを作成しようとします。 データ・レイアウトがコンポーネント型であり、ピクセルあたりのサンプル数がICCプロファイルで記述されたコンポーネントの数と等しいか、または1つ多い場合に使用されます。 ICCプロファイルが使用されない場合、色空間はaboveで説明された後続のステップの1つで推定されます。何らかの理由で、埋め込まれたICCプロファイルが自動的に使用されない場合は、この手順に従って手動で使用することができます:
ImageReader.getImageMetadata
からイメージ・メタデータを取得- ICCプロファイル・フィールドとその値を抽出します。
ICC_Profile.getInstance(byte[])
を使用してICCプロファイル・フィールド・データから作成したICC_ProfileからICC_ColorSpaceを作成します。ICC_ColorSpace
を受け入れるファクトリ・メソッドの1つを使用して、新しい色空間からImageTypeSpecifier
を作成します。- 互換性のあるImageReadParamを作成し、
ImageReadParam.setDestinationType
を使用してImageTypeSpecifier
を設定します。 - パラメータ・オブジェクトを適切な
read
メソッドに渡します。
「ICCプロファイル」フィールドに基づかない推定色空間がICCプロファイル・ベースの色空間と互換性がある場合、この推定色空間から派生した第2のImageTypeSpecifier
がImageReader.getImageTypes
によって返されるIteratorに含まれます。 イテレータに複数の型が含まれている場合、最初の型はICCプロファイルに基づいており、2番目の型は推定色空間に基づいています。
メタデータの問題
デフォルトでは、TIFFイメージ・ファイル・ディレクトリ(IFD)内の認識されたすべてのフィールドがネイティブ・イメージ・メタデータ・オブジェクトにロードされます。 どのフィールドが読み込まれるかは、読者が認識できるTIFFタグ、認識できないタグのフィールドを読み込むかどうか、およびすべてのメタデータを無視するかどうかによって決まります。 読者は、ImageReader.setInput(Object,boolean,boolean)
のignoreMetadata
パラメータを介して、通常どおりすべてのメタデータを無視するように通知されます。 TIFFImageReadParam.addAllowedTagSet(TIFFTagSet)
とTIFFImageReadParam.removeAllowedTagSet(TIFFTagSet)
を介して、どのTIFFTagが認識するかどうかを通知します。 ignoreMetadata
がtrue
の場合、イメージの読み取りに不可欠なメタデータのみがネイティブ・イメージ・メタデータ・オブジェクトにロードされます。 ignoreMetadata
がfalse
の場合、リーダーは、デフォルトでイメージを読み取るために不可欠なフィールドまたは許可されたTIFFTagSet
の1つに含まれるTIFFTag
を持つフィールドだけをネイティブ・イメージ・メタデータ・オブジェクトにロードします。 TIFFImageReadParam.setReadUnknownTags(boolean)
がパラメータtrue
で呼び出されたTIFFImageReadParam
を渡すことによって、許可されたTIFFTagSet
にないタグを持つフィールドの読み取りを強制することができます。
TIFFDirectoryオブジェクトを使用すると、メタデータ値へのアクセスが簡単になります。 TIFFDirectory
のインスタンスは、TIFFDirectory.createFromMetadata
メソッドを使用してTIFFリーダーによって返されたIIOMetadata
オブジェクトから作成されます。
TIFFネイティブ・イメージ・メタデータの標準メタデータ・フォーマットへのマッピング
「TIFFネイティブ・イメージ・メタデータ」からの標準メタデータ形式javax_imageio_1.0要素の導出は、次の表に示されています。標準メタデータ要素 | TIFFフィールドからの派生 |
---|---|
/Chroma/ColorSpaceType@name | PhotometricInterpretation: WhiteIsZero、BlackIsZero、TransparencyMask = "GRAY"; RGB、PaletteColor => "RGB"; CMYK => "CMYK"; YCbCr => "YCbCr"; CIELab、ICCLab => "Lab"。 |
/Chroma/NumChannels@value | SamplesPerPixel |
/Chroma/BlackIsZero@value | "TRUE" <=> PhotometricInterpretation => WhiteIsZero |
/Chroma/Palette | ColorMap |
/Compression/CompressionTypeName@value | 圧縮: 非圧縮=> "none"; CCITT 1D => "CCITT RLE";グループ3ファックス=> "CCITT T.4";グループ4ファックス=> "CCITT T.6"; LZW => "LZW"; JPEG => "Old JPEG";新しいJPEG => "JPEG"; Zlib =>> "ZLib"; PackBits => "PackBits"; Deflate => "Deflate"; Exif JPEG => "JPEG"。 |
/Compression/Lossless@value | 圧縮: JPEGまたは新しいJPEG => "FALSE";そうでなければ"TRUE"。 |
/Data/PlanarConfiguration@value | Chunky => "PixelInterleaved"; Planar => "PlaneInterleaved"。 |
/Data/SampleFormat@value | PhotometricInterpretation PaletteColor => "Index"; SampleFormat符号なし整数データ=> "UnsignedIntegral"; SampleFormat 2つの補数符号付き整数データ=> "SignedIntegral"; SampleFormat IEEE浮動小数点データ=> "Real";それ以外の要素は放出されません。 |
/Data/BitsPerSample@value | BitsPerSampleは空白で区切られたリストです。 |
/Data/SampleMSB@value | FillOrder: 左から右=> BitsPerSample-1のスペースで区切られたリスト。右から左=>スペースで区切られた0のリスト。 |
/Dimension/PixelAspectRatio@value | (1/XResolution)/(1/YResolution) |
/Dimension/ImageOrientation@value | Orientation |
/Dimension/HorizontalPixelSize@value | ResolutionUnitがNoneでない場合、1/XResolution(ミリ・メートル)。 |
/Dimension/VerticalPixelSize@value | ResolutionUnitがNoneでない場合、1/YResolution(ミリ・メートル)。 |
/Dimension/HorizontalPosition@value | ResolutionUnitがNoneでない場合のミリ秒単位の単位。 |
/Dimension/VerticalPosition@value | ResolutionUnitがNoneでない場合はYミリ・メートル単位で表します。 |
/Document/FormatVersion@value | 6.0 |
/Document/SubimageInterpretation@value | NewSubFileType: 透明度=> "TransparencyMask";縮小解像度=> "ReducedResolution";シングル・ページ=> "SinglePage"。 |
/Document/ImageCreationTime@value | DateTime |
/Text/TextEntry | DocumentName, ImageDescription, Make, Model, PageName, Software, Artist, HostComputer, InkNames, Copyright: /Text/TextEntry@keyword =フィールド名、/Text/TextEntry@value =フィールド値。 例: TIFF Software field => /Text/TextEntry@keyword = "ソフトウェア"、/Text/TextEntry@value =イメージの作成に使用されたソフトウェア・パッケージの名前とバージョン番号。 |
/Transparency/Alpha@value | ExtraSamples: 関連するアルファ=> "premultiplied";関連しないアルファ=> "nonpremultiplied"。 |
Exifイメージを読む
TIFFリーダーを使用して、圧縮されていないExifイメージ、または圧縮されたExifイメージのAPP1
マーカー・セグメントの内容を読み取ることができます。
解凍したExifイメージを読む
圧縮されていないExifイメージは、1ページまたは2ページの圧縮されていないTIFFイメージで、そのIFDおよびイメージ・データの内容が特定の順序になっています。 各画素は、測光解釈RGBまたはYCbCrを有する3つの8ビット・サンプルを有します。 イメージ・ストリームには1つのプライマリ・イメージが含まれていなければならず、単一のサムネイルが含まれている可能性があります。存在する場合は圧縮も解除する必要があります。 通常のImageReader
メソッドを使用して、イメージ・データとメタデータを読み取ることができます:
ImageInputStream input;
ImageReader tiffReader;
ImageReadParam tiffReadParam;
tiffReader.setInput(input);
// Read primary image and IFD.
BufferedImage image = tiffReader.read(0, tiffReadParam);
IIOMetadata primaryIFD = tiffReader.getImageMetadata(0);
// Read thumbnail if present.
BufferedImage thumbnail = null;
if (tiffReader.getNumImages(true) > 1) {
thumbnail = tiffReader.read(1, tiffReadParam);
}
Exifサムネイルはサムネイルとしてではなく、TIFFストリーム内の別のページとして扱われます。つまり、tiffReader.hasThumbnails(0)
はfalse
を返します。
圧縮されたExifイメージを読む
圧縮されたExifイメージは、APP1
マーカー・セグメントが挿入された3バンドISO/IEC 10918-1のベースラインDCT JPEGストリームです。 長さの後のマーカー・セグメントのパラメータは、完全なTIFFストリームに続く6バイトのシーケンス{'E', 'x', 'i', 'f', 0x00, 0x00}
です。 埋め込まれたTIFFストリームは、オプションでサムネイルIFDおよび圧縮または非圧縮のサムネイル・イメージ・データが続くJPEGイメージを記述するプライマリIFDを含む。 埋め込みTIFFストリームは、プライマリIFDに関連するイメージ・データも、JPEGストリーム自体に見られる情報を複製する記述フィールドも含まないことに留意されたい。
APP1
マーカー・セグメントのパラメータ内容は、JPEGリーダーによって返されたイメージ・メタデータ・オブジェクトから抽出されたjavax_imageio_jpeg_image_1.0
ネイティブ・イメージ・メタデータ・ツリー内の関連するNode
のユーザー・オブジェクトから取得することができます。 このAPP1 Exifノードは、"markerSequence"という名前のノードの子になり、名前unknown
と、0xE1
(String
の値"225"
)を持つMarkerTag
という名前の属性を持ちます。 このノードのユーザー・オブジェクトは、6バイトの{'E', 'x', 'i', 'f', '0', '0'}
で始まるバイト配列になります。 プライマリIFDおよびサムネイルIFDおよびイメージは、通常のImageReader
メソッドによってユーザー・オブジェクトから読み取ることができる:
ImageReader jpegReader;
ImageReader tiffReader;
// Obtain the APP1 Exif marker data from the JPEG image metadata.
IIOMetadata jpegImageMetadata = jpegReader.getImageMetadata(0);
String nativeFormat = jpegImageMetadata.getNativeMetadataFormatName();
Node jpegImageMetadataTree = jpegImageMetadata.getAsTree(nativeFormat);
// getExifMarkerData() returns the byte array which is the user object
// of the APP1 Exif marker node.
byte[] app1Params = getExifMarkerData(jpegImageMetadataTree);
if (app1Params == null) {
throw new IIOException("APP1 Exif marker not found.");
}
// Set up input, skipping Exif ID 6-byte sequence.
MemoryCacheImageInputStream app1ExifInput
= new MemoryCacheImageInputStream
(new ByteArrayInputStream(app1Params, 6, app1Params.length - 6));
tiffReader.setInput(app1ExifInput);
// Read primary IFD.
IIOMetadata primaryIFD = tiffReader.getImageMetadata(0);
// Read thumbnail if present.
BufferedImage thumbnail = null;
if (tiffReader.getNumImages(true) > 1) {
thumbnail = tiffReader.read(1, tiffReadParam);
}
// Read the primary image.
BufferedImage image = jpegReader.read(0);
tiffReader.getNumImages(true)
は、空のイメージに対応するものを含む組み込みTIFFストリーム内のIFDの数を返します。 埋め込まれたTIFFストリームのプライマリ・イメージは常に空であるため、tiffReader.read(0, readParam)
を呼び出すと例外がスローされます。主画像はJPEGリーダー自体を使用して取得する必要があります。
イメージを書く
TIFFイメージは、ImageWriterによって書き込まれます。ImageWriterは、公開されているImageWriteParamを介して公開されているインタフェースと同様に制御されます。 TIFFImageWriter
のgetDefaultWriteParam()
メソッドによって返されたImageWriteParam
に対して、canWriteTiles()
およびcanWriteCompressed()
メソッドはtrue
を返します。canOffsetTiles()
とcanWriteProgressive()
メソッドはfalse
を返します。 TIFFライターは、タイル・イメージの書き込み、イメージの挿入、空のイメージの書き込みまたは挿入、イメージ・データの置換など、多くのオプション機能をサポートしています。 ピクセルは、空のイメージまたは空でないイメージのいずれかで、データが圧縮されていない場合にのみ置き換えられます。
タイルが書き込まれている場合、各ディメンションはTIFF仕様に従って16の最も近い倍数に丸められます。 JPEG-in-TIFF圧縮が使用されており、タイルが書き込まれている場合、各タイルのディメンションは、そのディメンションのJPEG最小単位(MCU)の最も近い倍数に丸められます。 JPEG-in-TIFF圧縮が使用されていてストリップが書き込まれている場合、ストリップあたりの行数は、両方のディメンションで最大MCUの8倍の倍数に丸められます。
圧縮
圧縮モードは、圧縮モードをMODE_EXPLICIT
に設定した後、ImageWriteParam
のsetCompressionType()
メソッドによって設定することができます。 本来サポートされている圧縮形式のセットを次の表に示します:
圧縮タイプ | 説明 | 参照 |
---|---|---|
CCITT RLE | 修正ハフマン圧縮 | TIFF 6.0仕様、セクション10 |
CCITT T.4 | CCITT T.4二値符号化/グループ3ファクシミリ圧縮 | TIFF 6.0仕様、セクション11 |
CCITT T.6 | CCITT T.6二値符号化/グループ4ファクシミリ圧縮 | TIFF 6.0仕様、セクション11 |
LZW | LZW圧縮 | TIFF 6.0仕様、セクション13 |
JPEG | "新しい" JPEG-in-TIFF圧縮 | TIFFテクニカル・ノート#2 |
ZLib | "Deflate/Inflate" compression (この表の後の注記を参照) | |
PackBits | バイト指向、ランレングス圧縮 | TIFF 6.0仕様、セクション9 |
Deflate | "Zip-in-TIFF" compression (この表の後の注記を参照) | 「ZLIB圧縮データ・フォーマット仕様」, 「DEFLATE圧縮データ・フォーマット仕様」 |
Exif JPEG | Exif固有のJPEG圧縮(この表の後の注記を参照) | 「Exif 2.2仕様」 (PDF)、セクション4.5.5、"サムネイル・データの基本構造" |
TIFF 6.0仕様のセクション22で説明されている古いスタイルのJPEG圧縮はサポートされていません。
CCITT圧縮型は、二値の(1-bit)イメージにのみ適用されます。 JPEG圧縮型は、バイト・グレイ・スケールの(1-band)およびRGB (3-band)イメージにのみ適用されます。
ZLibとDeflate圧縮は、TIFF Compressionフィールドの値を除いて同一です: ZLibの圧縮フィールドの値は8ですが、Deflateの場合は値32946 (0x80b2)です。 どちらの場合も、各イメージ・セグメント(ストリップまたはタイル)は、単一の完全なzlibデータ・ストリームとして書き込まれます。
"Exif JPEG"は、JPEGネイティブ・イメージ・メタデータ・ツリーに含めるために、APP1 Exifマーカー・セグメントの内容を書き込むときに使用される圧縮型です。 この圧縮型が使用されたときに出力に付加される内容は、空のイメージまたは空でないイメージが書き込まれるかどうかの関数です。 イメージが空の場合、圧縮されたExifプライマリIFDの仕様に準拠したTIFF IFDが追加されます。 イメージが空でなければ、圧縮されたExifサムネイルIFDおよびイメージの仕様に従った完全なIFDおよびイメージが付加されます。 空のイメージのデータは、後にTIFFライターのピクセル置換機能を使用して追加できないことに注意してください。
ZLib/DeflateまたはJPEG圧縮を使用すると、圧縮品質が設定されます。 ZLib/Deflateの場合、指定された浮動小数点の品質値は[1, 9]
の範囲に再スケーリングされ、Deflate圧縮レベルを得るために整数に切り捨てられます。 JPEGの場合、浮動小数点品質値は通常の方法で解釈するJPEGライター・プラグ・インに直接渡されます。
色変換
ソース・イメージ・データ色空間型がRGBであり、宛先測光型がCIE L*a*b*またはYCbCrである場合、ソース・イメージ・データは内部カラー変換器を使用してRGBから自動的に変換されます。
ICCプロファイル
次のいずれかの場合、ICC Profile
フィールドが書き込まれます:
- 1つは、ライターに供給されたネイティブ・イメージ・メタデータIIOMetadataインスタンスに存在
- 宛先
ImageTypeSpecifier
のColorSpaceは、ColorSpace
クラスのCS_*
定数によって定義された標準色空間の1つではないICC_ColorSpace
のインスタンスです。 宛先型はImageWriteParam.setDestinationType(ImageTypeSpecifier)
で設定され、デフォルトでは書き込まれるイメージのImageTypeSpecifier
に設定されます。
メタデータの問題
ライターのいくつかの動作は、ユーザーによって供給されるイメージ・メタデータの内容によって影響されるか、または影響を受ける可能性があります。二値イメージの場合、FillOrder
、およびT4Options
フィールドは出力データに影響します。 FillOrder
の値が2 (BaselineTIFFTagSet.FILL_ORDER_RIGHT_TO_LEFT
)の場合、データは右から左に入力され、それ以外の場合は左から右に入力されます。 T4Options
の値は、データを1Dまたは2Dエンコードする必要があるかどうか、およびEOLパッディングを使用するかどうかを指定します。
すべてのイメージについて、RowsPerStrip
フィールドの値は、イメージがタイル化されていない場合、ストリップごとの行数を設定するために使用されます。 ストリップあたりの行のデフォルト数は、8または8キロバイトを超えないいずれか大きい方の行数です。
すべてのイメージについて、タイ・リング・モードがImageWriteParam.MODE_COPY_FROM_METADATA
の場合、TileWidth
およびTileLength
フィールド値を使用してタイル・ディメンションを設定できます。 このモードが設定されていてもフィールドが設定されていない場合、それぞれのデフォルト値はイメージの幅と高さです。
JPEG-in-TIFF圧縮を使用する場合、ライターに提供されたメタデータ・オブジェクトにJPEGTables
フィールドが含まれている場合にのみ、JPEGTables
フィールドがIFDに書き込まれ、JPEGストリームが各ストリップまたはタイルに短縮されます。 JPEGTables
フィールドの内容が有効なテーブルのみのJPEGストリームである場合は、それが使用されます。そうしないと、フィールドの内容はデフォルトの視覚的に無損失の表に置き換えられます。 そのようなJPEGTables
フィールドがメタデータに存在しない場合、JPEGTables
フィールドは出力に書き込まれず、各ストリップまたはタイルは独立した独立したJPEGストリームとして書き込まれます。
Deflate/ZLibまたはLZW圧縮を使用する場合、イメージが8ビット/サンプルの場合、Predictor
フィールドの値が2 (BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING
)の場合、水平差分予測子が使用されます。 予測がリクエストされても、イメージが8ビット/サンプルを持たない場合、フィールドは1 (BaselineTIFFTagSet.PREDICTOR_NONE
)の値にリセットされます。
いくつかのフィールドが追加または変更されることがあります:
PhotometricInterpretation
が存在しない場合。- このフィールドが値
Planar
で存在する場合、PlanarConfiguration
はChunky
にリセットされます。 Compression
always.- イメージが2レベルでない場合は
BitsPerSample
。 SamplesPerPixel
always.ExtraSamples
(アルファ・チャネルが存在する場合)。SampleFormat
が存在しない場合、そのデータは16ビットまたは32ビットの整数または浮動小数点です。PhotometricInterpretation
がRGBPalette
の場合はColorMap
。- 常に
ImageWidth
とImageLength
です。 TileWidth
、TileLength
、TileOffsets
、およびTileByteCounts
が含まれています。RowsPerStrip
、StripOffsets
、およびStripByteCounts
が含まれています。XResolution
、YResolution
、およびResolutionUnit
が存在しない場合。YCbCrSubsampling
とYCbCrPositioning
のように、測光の解釈がYCbCrで、圧縮のタイプがJPEGでない場合は(JPEG以外のYCbCr出力に対しては、[1, 1]サブ・サンプリングとサイズ測位のみがサポートされています)です。YCbCrSubsampling
、YCbCrPositioning
、およびReferenceBlackWhite
: 圧縮型がJPEGで色空間がRGBの場合、これらは、ヘッドルーム/フットルーム(0:255,128:255,128:255)を持たない[2, 2]中心のサブ・サンプリングにリセットされます。
いくつかのフィールドが削除されることがあります:
- イメージが2レベルの場合は
BitsPerSample
。 - イメージにアルファ・チャネルがない場合は
ExtraSamples
。 - 測光の解釈が
RGBPalette
でない場合はColorMap
。 TileWidth
、TileLength
、TileOffsets
、およびTileByteCounts
を使用しない場合は、。RowsPerStrip
、StripOffsets
、およびStripByteCounts
を使用しています。YCbCrSubsampling
、YCbCrPositioning
、およびReferenceBlackWhite
を指定します。圧縮タイプがJPEGで、色空間がグレースケールの場合。- 圧縮タイプがJPEGの場合、
JPEGProc
、JPEGInterchangeFormat
、JPEGInterchangeFormatLength
、JPEGRestartInterval
、JPEGLosslessPredictors
、JPEGPointTransforms
、JPEGQTables
、JPEGDCTables
、およびJPEGACTables
です。
提供されたメタデータに存在する他のフィールドは解釈されず、提供されたものとして書き込まれます。
Exifイメージが書き込まれている場合、存在するフィールドのセットとその値は、結果がExif 2.2仕様と一致するように変更されます。
TIFFストリームに書き込むためのイメージ・メタデータの設定は、TIFF IFDを表すTIFFDirectory
クラスを使用することで簡略化できます。 TIFF IFD内のフィールドは、TIFFFieldのインスタンスで表されます。 書き込まれる各フィールドに対して、TIFFField
をTIFFDirectory
に追加し、後者をTIFFDirectory.getAsMetadata
を呼び出してIIOMetadata
オブジェクトに変換することができます。 こうして得られたIIOMetadata
オブジェクトは、TIFFライターに渡されます。
TIFFネイティブ・イメージ・メタデータへの標準メタデータ・フォーマットのマッピング
「TIFFネイティブ・イメージ・メタデータ」要素の標準メタデータ形式javax_imageio_1.0からの派生は、次の表に示されています。TIFFフィールド | 標準メタデータ要素からの派生 |
---|---|
PhotometricInterpretation | /Chroma/ColorSpaceType@name: "GRAY"と/Chroma/BlackIsZero@value = "FALSE" => WhiteIsZero; "GRAY"および/Document/SubimageInterpretation@value = "TransparencyMask" => TransparencyMask; "RGB"と/Chroma/Palette present => PaletteColor; "GRAY" => BlackIsZero; "RGB" => RGB; "YCbCr" => YCbCr; "CMYK" => CMYK; "Lab" => CIELab。 |
SamplesPerPixel | /Chroma/NumChannels@value |
ColorMap | /Chroma/Palette |
Compression | /Compression/CompressionTypeName@value: "none" =>非圧縮; "CCITT RLE" => CCITT 1D; "CCITT T.4" =>グループ3ファックス; "CCITT T.6" =>グループ4ファックス; "LZW" => LZW; "Old JPEG" => JPEG; "JPEG" =>新JPEG; "ZLib" => ZLib; "PackBits" => PackBits; "Deflate" =>デフレート。 |
PlanarConfiguration | /Data/PlanarConfiguration@value: "PixelInterleaved" => Chunky; "PlaneInterleaved" => Planar。 |
SampleFormat | /Data/SampleFormat@value: "SignedIntegral" => 2つの補数符号付き整数データ。"UnsignedIntegral" =>符号なし整数データ。"Real" => IEEE浮動小数点データ。"Index" =>符号なし整数データ。 |
BitsPerSample | /Data/BitsPerSample@value: char配列に解析されたスペース区切りのリスト。 |
FillOrder | /Data/SampleMSB@value: スペースで区切られたリストのすべての値が0s =>右から左;そうでなければ=>左から右。 |
XResolution | (10 / /Dimension/HorizontalPixelSize@value)または(10 / (/Dimension/VerticalPixelSize@value * /Dimension/PixelAspectRatio@value)) |
YResolution | (10 / /Dimension/VerticalPixelSize@value)または(10 / (/Dimension/HorizontalPixelSize@value / /Dimension/PixelAspectRatio@value)) |
ResolutionUnit | XResolutionまたはYResolutionが設定されている場合はセンチメートル。それ以外はNone。 |
Orientation | /Dimension/ImageOrientation@value |
XPosition | /Dimension/HorizontalPosition@value / 10 |
YPosition | /Dimension/VerticalPosition@value / 10 |
NewSubFileType | /Document/SubimageInterpretation@value: "TransparencyMask" =>透過マスク。"ReducedResolution" =>解像度の低下。"SinglePage" =>シングル・ページ。 |
DateTime | /Document/ImageCreationTime@value |
DocumentName, ImageDescription, Make, Model, PageName, Software, Artist, HostComputer, InkNames, Copyright | /Text/TextEntry: /Text/TextEntry@keywordは、TIFFフィールドのいずれかの名前です(例:"ソフトウェア")。このフィールドは、content /Text/TextEntry@valueとcount 1で追加されます。 |
ExtraSamples | /Transparency/Alpha@value: "premultiplied" =>関連付けられたアルファ、カウント1; "nonpremultiplied" =>関連付けられていないアルファ、カウント1。 |
Exifイメージの作成
TIFFライターを使用して、圧縮されていないExifイメージ、または圧縮されたExifイメージのAPP1
マーカー・セグメントの内容を書き込むことができます。
非圧縮Exifイメージの作成
一連のイメージを書き込む場合、各イメージは通常、 {IFD, IFD Value, Image Data}として記録されます。 Exif仕様では、非圧縮Exifイメージを次のように構成する必要があります:- イメージ・ファイル・ヘッダー
- プライマリIFD
- プライマリIFD値
- サムネイルIFD
- サムネイルIFD値
- サムネイル・イメージ・データ
- プライマリ・イメージ・データ
ImageWriter tiffWriter;
ImageWriteParam tiffWriteParam;
IIOMetadata tiffStreamMetadata;
IIOMetadata primaryIFD;
BufferedImage image;
BufferedImage thumbnail;
// Specify uncompressed output.
tiffWriteParam.setCompressionMode(ImageWriteParam.MODE_DISABLED);
if (thumbnail != null) {
// Write the TIFF header.
tiffWriter.prepareWriteSequence(tiffStreamMetadata);
// Append the primary IFD.
tiffWriter.prepareInsertEmpty(-1, // append
new ImageTypeSpecifier(image),
image.getWidth(),
image.getHeight(),
primaryIFD,
null, // thumbnails
tiffWriteParam);
tiffWriter.endInsertEmpty();
// Append the thumbnail image data.
tiffWriter.writeToSequence(new IIOImage(thumbnail, null, null),
tiffWriteParam);
// Insert the primary image data.
tiffWriter.prepareReplacePixels(0, new Rectangle(image.getWidth(),
image.getHeight()));
tiffWriter.replacePixels(image, tiffWriteParam);
tiffWriter.endReplacePixels();
// End writing.
tiffWriter.endWriteSequence();
} else {
// Write only the primary IFD and image data.
tiffWriter.write(tiffStreamMetadata,
new IIOImage(image, null, primaryIFD),
tiffWriteParam);
}
圧縮されたExifイメージの作成
圧縮されたExifイメージのAPP1
セグメント内の埋め込みTIFFストリームの構造は、1次イメージ・データが存在しないことを除いて、「非圧縮Exifイメージ構造」と同じです。つまり、1次IFDはイメージ・データを参照しません。
ImageWriter tiffWriter;
ImageWriteParam tiffWriteParam;
IIOMetadata tiffStreamMetadata;
BufferedImage image;
BufferedImage thumbnail;
IIOMetadata primaryIFD;
ImageOutputStream output;
// Set up an output to contain the APP1 Exif TIFF stream.
ByteArrayOutputStream baos = new ByteArrayOutputStream();
MemoryCacheImageOutputStream app1ExifOutput =
new MemoryCacheImageOutputStream(baos);
tiffWriter.setOutput(app1ExifOutput);
// Set compression for the thumbnail.
tiffWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
tiffWriteParam.setCompressionType("Exif JPEG");
// Write the APP1 Exif TIFF stream.
if (thumbnail != null) {
// Write the TIFF header.
tiffWriter.prepareWriteSequence(tiffStreamMetadata);
// Append the primary IFD.
tiffWriter.prepareInsertEmpty(-1, // append
new ImageTypeSpecifier(image),
image.getWidth(),
image.getHeight(),
primaryIFD,
null, // thumbnails
tiffWriteParam);
tiffWriter.endInsertEmpty();
// Append the thumbnail IFD and image data.
tiffWriter.writeToSequence(new IIOImage(thumbnail, null,
null), tiffWriteParam);
// End writing.
tiffWriter.endWriteSequence();
} else {
// Write only the primary IFD.
tiffWriter.prepareWriteEmpty(tiffStreamMetadata,
new ImageTypeSpecifier(image),
image.getWidth(),
image.getHeight(),
primaryIFD,
null, // thumbnails
tiffWriteParam);
tiffWriter.endWriteEmpty();
}
// Flush data into byte stream.
app1ExifOutput.flush();
// Create APP1 parameter array.
byte[] app1Parameters = new byte[6 + baos.size()];
// Add APP1 Exif ID bytes.
app1Parameters[0] = (byte) 'E';
app1Parameters[1] = (byte) 'x';
app1Parameters[2] = (byte) 'i';
app1Parameters[3] = (byte) 'f';
app1Parameters[4] = app1Parameters[5] = (byte) 0;
// Append TIFF stream to APP1 parameters.
System.arraycopy(baos.toByteArray(), 0, app1Parameters, 6, baos.size());
// Create the APP1 Exif node to be added to native JPEG image metadata.
IIOMetadataNode app1Node = new IIOMetadataNode("unknown");
app1Node.setAttribute("MarkerTag", String.valueOf(0xE1));
app1Node.setUserObject(app1Parameters);
// Append the APP1 Exif marker to the "markerSequence" node.
IIOMetadata jpegImageMetadata =
jpegWriter.getDefaultImageMetadata(new ImageTypeSpecifier(image),
jpegWriteParam);
String nativeFormat = jpegImageMetadata.getNativeMetadataFormatName();
Node tree = jpegImageMetadata.getAsTree(nativeFormat);
NodeList children = tree.getChildNodes();
int numChildren = children.getLength();
for (int i = 0; i < numChildren; i++) {
Node child = children.item(i);
if (child.getNodeName().equals("markerSequence")) {
child.appendChild(app1Node);
break;
}
}
jpegImageMetadata.setFromTree(nativeFormat, tree);
// Write the JPEG image data including the APP1 Exif marker.
jpegWriter.setOutput(output);
jpegWriter.write(new IIOImage(image, null, jpegImageMetadata));
上記で作成された"unknown"
ノードは、ネイティブJPEGイメージ・メタデータの"markerSequence"
ノードに追加され、プライマリ・イメージがJPEGライターを使用して書き込まれるときにJPEGストリームに書き込まれます。
ストリーム・メタデータ
TIFFネイティブ・ストリーム・メタデータ形式のDTDは次のとおりです:<!DOCTYPE "javax_imageio_tiff_stream_1.0" [ <!ELEMENT "javax_imageio_tiff_stream_1.0" (ByteOrder)> <!ELEMENT "ByteOrder" EMPTY> <!-- The stream byte order --> <!ATTLIST "ByteOrder" "value" #CDATA #REQUIRED> <!-- One of "BIG_ENDIAN" or "LITTLE_ENDIAN" --> <!-- Data type: String --> ]>
イメージ・メタデータ
TIFFネイティブ・イメージ・メタデータ形式のDTDは次のとおりです:<!DOCTYPE "javax_imageio_tiff_image_1.0" [ <!ELEMENT "javax_imageio_tiff_image_1.0" (TIFFIFD)*> <!ELEMENT "TIFFIFD" (TIFFField | TIFFIFD)*> <!-- An IFD (directory) containing fields --> <!ATTLIST "TIFFIFD" "tagSets" #CDATA #REQUIRED> <!-- Data type: String --> <!ATTLIST "TIFFIFD" "parentTagNumber" #CDATA #IMPLIED> <!-- The tag number of the field pointing to this IFD --> <!-- Data type: Integer --> <!ATTLIST "TIFFIFD" "parentTagName" #CDATA #IMPLIED> <!-- A mnemonic name for the field pointing to this IFD, if known --> <!-- Data type: String --> <!ELEMENT "TIFFField" (TIFFBytes | TIFFAsciis | TIFFShorts | TIFFSShorts | TIFFLongs | TIFFSLongs | TIFFRationals | TIFFSRationals | TIFFFloats | TIFFDoubles | TIFFUndefined)> <!-- A field containing data --> <!ATTLIST "TIFFField" "number" #CDATA #REQUIRED> <!-- The tag number asociated with the field --> <!-- Data type: String --> <!ATTLIST "TIFFField" "name" #CDATA #IMPLIED> <!-- A mnemonic name associated with the field, if known --> <!-- Data type: String --> <!ELEMENT "TIFFBytes" (TIFFByte)*> <!-- A sequence of TIFFByte nodes --> <!ELEMENT "TIFFByte" EMPTY> <!-- An integral value between 0 and 255 --> <!ATTLIST "TIFFByte" "value" #CDATA #IMPLIED> <!-- The value --> <!-- Data type: String --> <!ATTLIST "TIFFByte" "description" #CDATA #IMPLIED> <!-- A description, if available --> <!-- Data type: String --> <!ELEMENT "TIFFAsciis" (TIFFAscii)*> <!-- A sequence of TIFFAscii nodes --> <!ELEMENT "TIFFAscii" EMPTY> <!-- A String value --> <!ATTLIST "TIFFAscii" "value" #CDATA #IMPLIED> <!-- The value --> <!-- Data type: String --> <!ELEMENT "TIFFShorts" (TIFFShort)*> <!-- A sequence of TIFFShort nodes --> <!ELEMENT "TIFFShort" EMPTY> <!-- An integral value between 0 and 65535 --> <!ATTLIST "TIFFShort" "value" #CDATA #IMPLIED> <!-- The value --> <!-- Data type: String --> <!ATTLIST "TIFFShort" "description" #CDATA #IMPLIED> <!-- A description, if available --> <!-- Data type: String --> <!ELEMENT "TIFFSShorts" (TIFFSShort)*> <!-- A sequence of TIFFSShort nodes --> <!ELEMENT "TIFFSShort" EMPTY> <!-- An integral value between -32768 and 32767 --> <!ATTLIST "TIFFSShort" "value" #CDATA #IMPLIED> <!-- The value --> <!-- Data type: String --> <!ATTLIST "TIFFSShort" "description" #CDATA #IMPLIED> <!-- A description, if available --> <!-- Data type: String --> <!ELEMENT "TIFFLongs" (TIFFLong)*> <!-- A sequence of TIFFLong nodes --> <!ELEMENT "TIFFLong" EMPTY> <!-- An integral value between 0 and 4294967295 --> <!ATTLIST "TIFFLong" "value" #CDATA #IMPLIED> <!-- The value --> <!-- Data type: String --> <!ATTLIST "TIFFLong" "description" #CDATA #IMPLIED> <!-- A description, if available --> <!-- Data type: String --> <!ELEMENT "TIFFSLongs" (TIFFSLong)*> <!-- A sequence of TIFFSLong nodes --> <!ELEMENT "TIFFSLong" EMPTY> <!-- An integral value between -2147483648 and 2147482647 --> <!ATTLIST "TIFFSLong" "value" #CDATA #IMPLIED> <!-- The value --> <!-- Data type: String --> <!ATTLIST "TIFFSLong" "description" #CDATA #IMPLIED> <!-- A description, if available --> <!-- Data type: String --> <!ELEMENT "TIFFRationals" (TIFFRational)*> <!-- A sequence of TIFFRational nodes --> <!ELEMENT "TIFFRational" EMPTY> <!-- A rational value consisting of an unsigned numerator and denominator --> <!ATTLIST "TIFFRational" "value" #CDATA #IMPLIED> <!-- The numerator and denominator, separated by a slash --> <!-- Data type: String --> <!ELEMENT "TIFFSRationals" (TIFFSRational)*> <!-- A sequence of TIFFSRational nodes --> <!ELEMENT "TIFFSRational" EMPTY> <!-- A rational value consisting of a signed numerator and denominator --> <!ATTLIST "TIFFSRational" "value" #CDATA #IMPLIED> <!-- The numerator and denominator, separated by a slash --> <!-- Data type: String --> <!ELEMENT "TIFFFloats" (TIFFFloat)*> <!-- A sequence of TIFFFloat nodes --> <!ELEMENT "TIFFFloat" EMPTY> <!-- A single-precision floating-point value --> <!ATTLIST "TIFFFloat" "value" #CDATA #IMPLIED> <!-- The value --> <!-- Data type: String --> <!ELEMENT "TIFFDoubles" (TIFFDouble)*> <!-- A sequence of TIFFDouble nodes --> <!ELEMENT "TIFFDouble" EMPTY> <!-- A double-precision floating-point value --> <!ATTLIST "TIFFDouble" "value" #CDATA #IMPLIED> <!-- The value --> <!-- Data type: String --> <!ELEMENT "TIFFUndefined" EMPTY> <!-- Uninterpreted byte data --> <!ATTLIST "TIFFUndefined" "value" #CDATA #IMPLIED> <!-- A list of comma-separated byte values --> <!-- Data type: String --> ]>
- 導入されたバージョン:
- 9