다음을 통해 공유


메타데이터 읽기 및 쓰기

일부 이미지 파일에는 이미지의 기능을 확인하기 위해 읽을 수 있는 메타데이터가 포함되어 있습니다. 예를 들어 디지털 사진에는 이미지를 캡처하는 데 사용되는 카메라의 메이크 및 모델을 결정하기 위해 읽을 수 있는 메타데이터가 포함될 수 있습니다. Windows GDI+를 사용하면 기존 메타데이터를 읽을 수 있으며 이미지 파일에 새 메타데이터를 작성할 수도 있습니다.

GDI+는 이미지 파일의 메타데이터를 다양한 형식으로 저장하고 검색하는 균일한 방법을 제공합니다. GDI+에서 메타데이터 조각을 속성 항목이라고 합니다. Image 클래스의 SetPropertyItemGetPropertyItem 메서드를 호출하여 메타데이터를 저장하고 검색할 수 있으며 특정 파일 형식이 해당 메타데이터를 저장하는 방법에 대한 세부 정보는 걱정할 필요가 없습니다.

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개의 공용 멤버가 있습니다.

Description
id 메타데이터 항목을 식별하는 태그입니다. id에 할당할 수 있는 값(PropertyTagImageTitle, PropertyTagEquipMake, PropertyTagExifExposureTime 등)은 Gdiplusimaging.h에 정의되어 있습니다.
length 데이터 멤버가 가리키는 값 배열의 길이(바이트)입니다. 형식 데이터 멤버가 PropertyTagTypeASCII로 설정된 경우 길이 데이터 멤버는 NULL 종결자를 포함하여 null로 끝나는 문자열의 길이입니다.
type 값 데이터 멤버가 가리키는 배열에 있는 값의 데이터 형식입니다. 다양한 데이터 형식을 나타내는 상수(PropertyTagTypeByte, PropertyTagTypeASCII 등)는 이미지 속성 태그 형식 상수에 설명되어 있습니다.
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 번호를 조회하고 다음 속성 태그를 나타내는지 확인할 수 있습니다.

16 진수 값 속성 태그
0x0320 0x010f
  0x0110
  0x9003
  0x829a
  0x5090
  0x5091
PropertyTagImageTitle PropertyTagEquipMake
  PropertyTagEquipModel
  PropertyTagExifDTOriginal
  PropertyTagExifExposureTime
  PropertyTagLuminanceTable
  PropertyTagChrominanceTable

 

목록의 두 번째(인덱스 1) 속성 항목에는 id PropertyTagEquipMake가 있고 PropertyTagTypeASCII 를 입력 합니다. 이전 콘솔 애플리케이션의 연속인 다음 예제에서는 해당 속성 항목의 값을 표시합니다.

printf("The equipment make is %s.\n", pPropBuffer[1].value);

위의 코드 줄은 다음 출력을 생성합니다.

The equipment make is Northwind Traders.

목록의 다섯 번째(인덱스 4) 속성 항목에는 id PropertyTagExifExposureTime이 있고 PropertyTagTypeRational 을 입력 합니다. 해당 데이터 형식(PropertyTagTypeRational)은 LONG의 쌍입니다. 이전 콘솔 애플리케이션의 연속인 다음 예제에서는 이러한 두 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 메서드에 전달합니다.

다음 콘솔 애플리케이션은 메타데이터의 한 항목(이미지 제목)을 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;
}