メタデータの読み取りと書き込み

一部の画像ファイルにはメタデータが含まれており、これを読み取って画像の特徴を判断することができます。 たとえば、デジタル写真には、画像の撮影に使用されたカメラのメーカーとモデルを読み取って判別できるメタデータが含まれている場合があります。 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;
}