メタデータの読み取りと書き込み
一部の画像ファイルにはメタデータが含まれており、これを読み取って画像の特徴を判断することができます。 たとえば、デジタル写真には、画像の撮影に使用されたカメラのメーカーとモデルを読み取って判別できるメタデータが含まれている場合があります。 Windows GDI+ を使用すると、既存のメタデータを読み取ることができ、イメージ ファイルに新しいメタデータを書き込むこともできます。
GDI+ は、さまざまな形式の画像ファイルからメタデータを格納および取得するための統一された方法を提供します。 GDI+ では、メタデータの一部は プロパティ項目と呼ばれます。 Image クラスの SetPropertyItem メソッドと GetPropertyItem メソッドを呼び出すことでメタデータを格納および取得できます。また、特定のファイル形式でそのメタデータを格納する方法の詳細について心配する必要はありません。
GDI+ は現在、TIFF、JPEG、Exif、PNG ファイル形式のメタデータをサポートしています。 デジタル スチル カメラによってキャプチャされた画像を格納する方法を指定する Exif 形式は、TIFF および JPEG 形式の上に構築されています。 Exif では、圧縮されていないピクセル データには TIFF 形式を使用し、圧縮ピクセル データには JPEG 形式を使用します。
GDI+ は、プロパティ項目を識別するプロパティ タグのセットを定義します。 特定のタグは汎用です。つまり、前の段落で説明したすべてのファイル形式でサポートされています。 その他のタグは特殊な用途であり、特定の形式にのみ適用されます。 プロパティ項目を、そのプロパティ項目をサポートしていないファイルに保存しようとすると、GDI+ は要求を無視します。 具体的には、 Image::SetPropertyItem メソッドは PropertyNotSupported を返します。
Image::GetPropertyIdList を呼び出すことで、イメージ ファイルに格納されているプロパティ項目を確認できます。 ファイルにないプロパティ項目を取得しようとすると、GDI+ は要求を無視します。 具体的には、 Image::GetPropertyItem メソッドは PropertyNotFound を返します。
ファイルからのメタデータの読み取り
次のコンソール アプリケーションは、Image オブジェクトの GetPropertySize メソッドを呼び出して、ファイル FakePhoto.jpg内のメタデータの数を決定します。
#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>
using namespace Gdiplus;
INT main()
{
// Initialize <tla rid="tla_gdiplus"/>.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
UINT size = 0;
UINT count = 0;
Bitmap* bitmap = new Bitmap(L"FakePhoto.jpg");
bitmap->GetPropertySize(&size, &count);
printf("There are %d pieces of metadata in the file.\n", count);
printf("The total size of the metadata is %d bytes.\n", size);
delete bitmap;
GdiplusShutdown(gdiplusToken);
return 0;
}
上記のコードは、特定のファイルと共にFakePhoto.jpg、次の出力を生成しました。
There are 7 pieces of metadata in the file.
The total size of the metadata is 436 bytes.
GDI+ は、個々のメタデータを PropertyItem オブジェクトに格納します。 Image クラスの GetAllPropertyItems メソッドを呼び出して、ファイルからすべてのメタデータを取得できます。 GetAllPropertyItems メソッドは、PropertyItem オブジェクトの配列を返します。 GetAllPropertyItems を呼び出す前に、その配列を受け取るのに十分な大きさのバッファーを割り当てる必要があります。 Image クラスの GetPropertySize メソッドを呼び出して、必要なバッファーのサイズ (バイト単位) を取得できます。
PropertyItem オブジェクトには、次の 4 つのパブリック メンバーがあります。
説明 | |
---|---|
id | メタデータ項目を識別するタグ。 id に割り当てることができる値 (PropertyTagImageTitle、PropertyTagEquipMake、PropertyTagExifExposureTime など) は、Gdiplusimaging.h で定義されています。 |
length | 値データ メンバーが指す値の配列の長さ (バイト単位)。 型データ メンバーが PropertyTagTypeASCII に設定されている場合、長さデータ メンバーは NULL 終端文字を含む null で終わる文字列の長さであることに注意してください。 |
type | 値データ メンバーが指す配列内の値のデータ型。 さまざまなデータ型を表す定数 (PropertyTagTypeByte、PropertyTagTypeASCII など) については、「 Image Property Tag Type Constants」を参照してください。 |
value | 値の配列へのポインター。 |
次のコンソール アプリケーションは、ファイル FakePhoto.jpg内の 7 つのメタデータを読み取って表示します。 メイン関数は、ヘルパー関数 PropertyTypeFromWORD に依存しています。これは、メイン関数の後に示されています。
#include <windows.h>
#include <gdiplus.h>
#include <strsafe.h>
using namespace Gdiplus;
INT main()
{
// Initialize GDI+
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
UINT size = 0;
UINT count = 0;
#define MAX_PROPTYPE_SIZE 30
WCHAR strPropertyType[MAX_PROPTYPE_SIZE] = L"";
Bitmap* bitmap = new Bitmap(L"FakePhoto.jpg");
bitmap->GetPropertySize(&size, &count);
printf("There are %d pieces of metadata in the file.\n\n", count);
// GetAllPropertyItems returns an array of PropertyItem objects.
// Allocate a buffer large enough to receive that array.
PropertyItem* pPropBuffer =(PropertyItem*)malloc(size);
// Get the array of PropertyItem objects.
bitmap->GetAllPropertyItems(size, count, pPropBuffer);
// For each PropertyItem in the array, display the id, type, and length.
for(UINT j = 0; j < count; ++j)
{
// Convert the property type from a WORD to a string.
PropertyTypeFromWORD(
pPropBuffer[j].type, strPropertyType, MAX_PROPTYPE_SIZE);
printf("Property Item %d\n", j);
printf(" id: 0x%x\n", pPropBuffer[j].id);
wprintf(L" type: %s\n", strPropertyType);
printf(" length: %d bytes\n\n", pPropBuffer[j].length);
}
free(pPropBuffer);
delete bitmap;
GdiplusShutdown(gdiplusToken);
return 0;
} // main
// Helper function
HRESULT PropertyTypeFromWORD(WORD index, WCHAR* string, UINT maxChars)
{
HRESULT hr = E_FAIL;
WCHAR* propertyTypes[] = {
L"Nothing", // 0
L"PropertyTagTypeByte", // 1
L"PropertyTagTypeASCII", // 2
L"PropertyTagTypeShort", // 3
L"PropertyTagTypeLong", // 4
L"PropertyTagTypeRational", // 5
L"Nothing", // 6
L"PropertyTagTypeUndefined", // 7
L"Nothing", // 8
L"PropertyTagTypeSLONG", // 9
L"PropertyTagTypeSRational"}; // 10
hr = StringCchCopyW(string, maxChars, propertyTypes[index]);
return hr;
}
上記のコンソール アプリケーションでは、次の出力が生成されます。
Property Item 0
id: 0x320
type: PropertyTagTypeASCII
length: 16 bytes
Property Item 1
id: 0x10f
type: PropertyTagTypeASCII
length: 17 bytes
Property Item 2
id: 0x110
type: PropertyTagTypeASCII
length: 7 bytes
Property Item 3
id: 0x9003
type: PropertyTagTypeASCII
length: 20 bytes
Property Item 4
id: 0x829a
type: PropertyTagTypeRational
length: 8 bytes
Property Item 5
id: 0x5090
type: PropertyTagTypeShort
length: 128 bytes
Property Item 6
id: 0x5091
type: PropertyTagTypeShort
length: 128 bytes
上記の出力は、各プロパティ項目の 16 進数の ID 番号を示しています。 これらの ID 番号は 、Image Property Tag 定数 で調べて、次のプロパティ タグを表していることを確認できます。
16 進数値 | プロパティ タグ |
---|---|
0x0320 0x010f 0x0110 0x9003 0x829a 0x5090 0x5091 |
PropertyTagImageTitle プロパティタグEquipMake PropertyTagEquipModel PropertyTagExifDTOriginal PropertyTagExifExposureTime PropertyTagLuminanceTable PropertyTagChrominanceTable |
リスト内の 2 番目の (インデックス 1) プロパティ項目には id PropertyTagEquipMake があり、 PropertyTagTypeASCII 型 です。 次の例は、前のコンソール アプリケーションの継続であり、そのプロパティ項目の値を表示します。
printf("The equipment make is %s.\n", pPropBuffer[1].value);
上記のコード行では、次の出力が生成されます。
The equipment make is Northwind Traders.
リスト内の 5 番目の (インデックス 4) プロパティ項目には 、id PropertyTagExifExposureTime と 型 PropertyTagTypeRational があります。 そのデータ型 (PropertyTagTypeRational) は LONGのペアです。 次の例は、前のコンソール アプリケーションの継続であり、これら 2 つの LONG 値を分数として表示します。 すなわち分画は、1/125、秒で測定された露光時間である。
long* ptrLong = (long*)(pPropBuffer[4].value);
printf("The exposure time is %d/%d.\n", ptrLong[0], ptrLong[1]);
上のコードを実行すると、次の出力が生成されます。
The exposure time is 1/125.
ファイルへのメタデータの書き込み
メタデータの項目を Image オブジェクトに書き込むには、PropertyItem オブジェクトを初期化し、その PropertyItem オブジェクトのアドレスを Image オブジェクトの SetPropertyItem メソッドに渡します。
次のコンソール アプリケーションは、メタデータの 1 つの項目 (イメージ タイトル) を Image オブジェクトに書き込み、そのイメージをディスク ファイル FakePhoto2.jpgに保存します。 メイン関数は、ヘルパー関数 GetEncoderClsid に依存しています。これは、「エンコーダーのクラス識別子の取得」のトピックに示されています。
#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>
using namespace Gdiplus;
INT main()
{
// Initialize <tla rid="tla_gdiplus"/>.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
Status stat;
CLSID clsid;
char propertyValue[] = "Fake Photograph";
Bitmap* bitmap = new Bitmap(L"FakePhoto.jpg");
PropertyItem* propertyItem = new PropertyItem;
// Get the CLSID of the JPEG encoder.
GetEncoderClsid(L"image/jpeg", &clsid);
propertyItem->id = PropertyTagImageTitle;
propertyItem->length = 16; // string length including NULL terminator
propertyItem->type = PropertyTagTypeASCII;
propertyItem->value = propertyValue;
bitmap->SetPropertyItem(propertyItem);
stat = bitmap->Save(L"FakePhoto2.jpg", &clsid, NULL);
if(stat == Ok)
printf("FakePhoto2.jpg saved successfully.\n");
delete propertyItem;
delete bitmap;
GdiplusShutdown(gdiplusToken);
return 0;
}