Чтение и запись метаданных
Некоторые файлы изображений содержат метаданные, которые можно считывать для определения функций образа. Например, цифровая фотография может содержать метаданные, которые можно считывать для определения создания и модели камеры, используемой для захвата изображения. С помощью Windows GDI+можно считывать существующие метаданные, а также записывать новые метаданные в файлы изображений.
GDI+ предоставляет универсальный способ хранения и получения метаданных из файлов изображений в различных форматах. В GDI+ часть метаданных называется элементом свойства . Вы можете хранить и извлекать метаданные, вызывая методы SetPropertyItem и GetPropertyItem класса Image, и вам не нужно беспокоиться о том, как конкретный формат файлов хранит эти метаданные.
GDI+ в настоящее время поддерживает метаданные форматов TIFF, JPEG, Exif и PNG. Формат Exif, который указывает, как хранить изображения, захваченные цифровыми камерами, построен на основе форматов TIFF и JPEG. Exif использует формат TIFF для несжатых данных пикселей и формата JPEG для сжатых данных пикселей.
GDI+ определяет набор тегов свойств, определяющих элементы свойств. Некоторые теги являются общими; То есть они поддерживаются всеми форматами файлов, упомянутыми в предыдущем абзаце. Другие теги являются специальными и применяются только к определенным форматам. При попытке сохранить элемент свойства в файл, который не поддерживает этот элемент свойства, GDI+ игнорирует запрос. Метод Image::SetPropertyItem возвращает СвойствоНеПоддерживается.
Вы можете определить элементы свойств, хранящиеся в файле изображения, вызвав Image::GetPropertyIdList. При попытке получить элемент свойства, который не находится в файле, GDI+ игнорирует запрос. В частности, метод Image::GetPropertyItem возвращает PropertyNotFound.
Чтение метаданных из файла
Следующее консольное приложение вызывает метод GetPropertySize объекта Image, чтобы определить, сколько фрагментов метаданных находятся в файле 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. Чтобы получить все метаданные из файла, можно вызвать метод GetAllPropertyItems класса Image. Метод GetAllPropertyItems возвращает массив объектов PropertyItem. Перед вызовом GetAllPropertyItemsнеобходимо выделить буфер достаточно большой для получения этого массива. Можно вызвать метод GetPropertySize класса Image, чтобы получить размер (в байтах) требуемого буфера.
Объект PropertyItem имеет следующие четыре открытых члена:
Описание | |
---|---|
идентификатор | Тег, определяющий элемент метаданных. Значения, которые можно назначить идентификатору (PropertyTagImageTitle, PropertyTagEquipMake, PropertyTagExif ExposureTime и подобное), определяются в Gdiplusimaging.h. |
длина | Длина в байтах массива значений, на который указывает элемент данных значение. Обратите внимание, что если для элемента данных типа задано значение PropertyTagTypeASCII, то элемент данных длины — это длина символьной строки, завершаемой значением NULL, включая конечный элемент NULL. |
типа | Тип данных значений в массиве, на который указывает элемент данных значения. Константы (PropertyTagTypeByte, PropertyTagTypeASCII и подобные), представляющие различные типы данных, описаны в константы тегов свойств изображения. |
значения | Указатель на массив значений. |
Следующее консольное приложение считывает и отображает семь фрагментов метаданных в файле FakePhoto.jpg. Основная функция зависит от вспомогательной функции 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
В предыдущем выводе показан шестнадцатеричный номер для каждого элемента свойства. Идентификационные номера можно найти в разделе Константы тегов свойств изображения, где указано, что они представляют следующие теги свойств.
Шестнадцатеричное значение | Тег свойства |
---|---|
0x0320 0x010f 0x0110 0x9003 0x829a 0x5090 0x5091 |
ТегИзображенияНазвание ТегОборудованияПроизводитель PropertyTagEquipModel PropertyTagExifDTOriginal PropertyTagExif ExposureTime PropertyTagLuminanceTable PropertyTagChrominanceTable |
Второй элемент свойства (индекс 1) в списке имеет идентификатор PropertyTagEquipMake и тип PropertyTagTypeASCII. В следующем примере, который является продолжением предыдущего консольного приложения, отображается значение этого элемента свойства:
printf("The equipment make is %s.\n", pPropBuffer[1].value);
Предыдущая строка кода создает следующие выходные данные:
The equipment make is Northwind Traders.
Пятый элемент свойства (индекс 4) в списке имеет идентификатор PropertyTagExif ExposureTime и тип 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 методу SetPropertyItem объекта Image.
Следующее консольное приложение записывает один элемент (заголовок изображения) метаданных в объект 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;
}