次の例では、イメージとそのメタデータを同じ形式の新しいファイルに再エンコードする方法を示します。 さらに、この例では、クエリ ライターによって使用される単一項目式を示すメタデータを追加します。
このトピックは、次のセクションで構成されています。
- 前提条件
- パート 1: イメージをデコードする
- パート 2: イメージ エンコーダーの作成と初期化
- パート 3: デコードされたフレーム情報をコピーする
- パート 4: メタデータをコピーする
- パート 5: メタデータの追加
- パート 6: エンコードされたイメージの最終処理
- JPEG 再エンコードのサンプル コード
- 関連トピック
[前提条件]
このトピックを理解するには、「 WIC メタデータの概要」の説明に従って、Windows イメージング コンポーネント (WIC) メタデータ システムについて理解している必要があります。 「 Windows イメージング コンポーネントの概要」の説明に従って、WIC コーデック コンポーネントについても理解しておく必要があります。
パート 1: イメージをデコードする
イメージ データまたはメタデータを新しいイメージ ファイルにコピーするには、まず、再エンコードする既存のイメージのデコーダーを作成する必要があります。 次のコードは、test.jpgイメージ ファイルの WIC デコーダーを作成する方法を示しています。
// Initialize COM.
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
IWICImagingFactory *piFactory = NULL;
IWICBitmapDecoder *piDecoder = NULL;
// Create the COM imaging factory.
if (SUCCEEDED(hr))
{
hr = CoCreateInstance(CLSID_WICImagingFactory,
NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&piFactory));
}
// Create the decoder.
if (SUCCEEDED(hr))
{
hr = piFactory->CreateDecoderFromFilename(L"test.jpg", NULL, GENERIC_READ,
WICDecodeMetadataCacheOnDemand, //For JPEG lossless decoding/encoding.
&piDecoder);
}
CreateDecoderFromFilename の呼び出しでは、WICDecodeOptions 列挙からの値 WICDecodeMetadataCacheOnDemand が 4 番目のパラメーターとして使用されました。 これにより、クエリ リーダーを取得するか、基になるメタデータ リーダーを使用して、メタデータが必要になったときにメタデータをキャッシュするようにデコーダーに指示します。 このオプションを使用すると、高速メタデータ エンコードを実行するために必要なメタデータへのストリームを保持でき、JPEG 画像の無損失デコードとエンコードが可能になります。 または、他の WICDecodeOptions 値 WICDecodeMetadataCacheOnLoad を使用することもできます。この値は、イメージが読み込まれるとすぐに埋め込まれたイメージ メタデータをキャッシュします。
パート 2: イメージ エンコーダーの作成と初期化
次のコードは、前にデコードしたイメージのエンコードに使用するエンコーダーの作成を示しています。
// Variables used for encoding.
IWICStream *piFileStream = NULL;
IWICBitmapEncoder *piEncoder = NULL;
IWICMetadataBlockWriter *piBlockWriter = NULL;
IWICMetadataBlockReader *piBlockReader = NULL;
WICPixelFormatGUID pixelFormat = { 0 };
UINT count = 0;
double dpiX, dpiY = 0.0;
UINT width, height = 0;
// Create a file stream.
if (SUCCEEDED(hr))
{
hr = piFactory->CreateStream(&piFileStream);
}
// Initialize our new file stream.
if (SUCCEEDED(hr))
{
hr = piFileStream->InitializeFromFilename(L"test2.jpg", GENERIC_WRITE);
}
// Create the encoder.
if (SUCCEEDED(hr))
{
hr = piFactory->CreateEncoder(GUID_ContainerFormatJpeg, NULL, &piEncoder);
}
// Initialize the encoder
if (SUCCEEDED(hr))
{
hr = piEncoder->Initialize(piFileStream,WICBitmapEncoderNoCache);
}
WIC ファイル ストリーム piFileStream が作成され、イメージ ファイル "test2.jpg" に書き込まれるため初期化されます。 その後、piFileStream を使用してエンコーダーを初期化し、エンコードが完了したときにイメージ ビットを書き込む場所をエンコーダーに通知します。
パート 3: デコードされたフレーム情報をコピーする
次のコードは、画像の各フレームをエンコーダーの新しいフレームにコピーします。 このコピーには、サイズ、解像度、ピクセル形式が含まれます。これらはすべて有効なフレームを作成するために必要です。
注
JPEG 画像にはフレームが 1 つだけあり、以下のループは技術的には必要ありませんが、それをサポートする形式のマルチフレームの使用方法を示すために含まれています。
if (SUCCEEDED(hr))
{
hr = piDecoder->GetFrameCount(&count);
}
if (SUCCEEDED(hr))
{
// Process each frame of the image.
for (UINT i=0; i<count && SUCCEEDED(hr); i++)
{
// Frame variables.
IWICBitmapFrameDecode *piFrameDecode = NULL;
IWICBitmapFrameEncode *piFrameEncode = NULL;
IWICMetadataQueryReader *piFrameQReader = NULL;
IWICMetadataQueryWriter *piFrameQWriter = NULL;
// Get and create the image frame.
if (SUCCEEDED(hr))
{
hr = piDecoder->GetFrame(i, &piFrameDecode);
}
if (SUCCEEDED(hr))
{
hr = piEncoder->CreateNewFrame(&piFrameEncode, NULL);
}
// Initialize the encoder.
if (SUCCEEDED(hr))
{
hr = piFrameEncode->Initialize(NULL);
}
// Get and set the size.
if (SUCCEEDED(hr))
{
hr = piFrameDecode->GetSize(&width, &height);
}
if (SUCCEEDED(hr))
{
hr = piFrameEncode->SetSize(width, height);
}
// Get and set the resolution.
if (SUCCEEDED(hr))
{
piFrameDecode->GetResolution(&dpiX, &dpiY);
}
if (SUCCEEDED(hr))
{
hr = piFrameEncode->SetResolution(dpiX, dpiY);
}
// Set the pixel format.
if (SUCCEEDED(hr))
{
piFrameDecode->GetPixelFormat(&pixelFormat);
}
if (SUCCEEDED(hr))
{
hr = piFrameEncode->SetPixelFormat(&pixelFormat);
}
次のコードでは、ソースとコピー先のイメージの形式が同じかどうかを判断するためのクイック チェックを実行します。 これは、パート 4 で、ソースと宛先の形式が同じ場合にのみサポートされる操作を示すので必要です。
// Check that the destination format and source formats are the same.
bool formatsEqual = FALSE;
if (SUCCEEDED(hr))
{
GUID srcFormat;
GUID destFormat;
hr = piDecoder->GetContainerFormat(&srcFormat);
if (SUCCEEDED(hr))
{
hr = piEncoder->GetContainerFormat(&destFormat);
}
if (SUCCEEDED(hr))
{
if (srcFormat == destFormat)
formatsEqual = true;
else
formatsEqual = false;
}
}
パート 4: メタデータをコピーする
注
このセクションのコードは、コピー元とコピー先のイメージの形式が同じ場合にのみ有効です。 別のイメージ形式にエンコードする場合、イメージのすべてのメタデータを 1 回の操作でコピーすることはできません。
イメージを同じイメージ形式に再エンコードするときにメタデータを保持するために、すべてのメタデータを 1 回の操作でコピーできるメソッドがあります。 これらの各操作は、同様のパターンに従います。デコードされたフレームのメタデータは、エンコードされる新しいフレームに直接設定されます。 これは、個々のイメージ フレームごとに行われることに注意してください。
メタデータをコピーする場合は、デコードされたフレームのブロック リーダーを使用して、新しいフレームのブロック ライターを初期化することをお勧めします。 次のコードは、このメソッドを示しています。
if (SUCCEEDED(hr) && formatsEqual)
{
// Copy metadata using metadata block reader/writer.
if (SUCCEEDED(hr))
{
piFrameDecode->QueryInterface(IID_PPV_ARGS(&piBlockReader));
}
if (SUCCEEDED(hr))
{
piFrameEncode->QueryInterface(IID_PPV_ARGS(&piBlockWriter));
}
if (SUCCEEDED(hr))
{
piBlockWriter->InitializeFromBlockReader(piBlockReader);
}
}
この例では、ソース フレームと宛先フレームからそれぞれブロック リーダーとブロック ライターを取得します。 ブロック ライターは、ブロック リーダーから初期化されます。 これにより、ブロック リーダーの事前設定されたメタデータを使用してブロック ライターが初期化されます。 メタデータをコピーするためのその他の方法については、「イメージ メタデータの読み取りと書き込みの概要」の「メタデータ の書き込み」セクションを参照してください。
ここでも、この操作は、ソース イメージとコピー先イメージの形式が同じである場合にのみ機能します。 これは、メタデータ ブロックが異なる場所に格納されるイメージ形式が異なるためです。 たとえば、JPEG とタグ付きイメージ ファイル形式 (TIFF) の両方で、拡張メタデータ プラットフォーム (XMP) メタデータ ブロックがサポートされます。 JPEG 画像では、「 WIC メタデータの概要」に示すように、XMP ブロックはルート メタデータ ブロックにあります。 ただし、TIFF イメージでは、XMP ブロックはルート IFD ブロックに埋め込まれます。
パート 5: メタデータの追加
次の例では、メタデータをコピー先イメージに追加する方法を示します。 これを行うには、クエリ式と PROPVARIANT に格納されているデータを使用して、クエリ ライターの SetMetadataByName メソッドを呼び出します。
if(SUCCEEDED(hr))
{
hr = piFrameEncode->GetMetadataQueryWriter(&piFrameQWriter);
}
if (SUCCEEDED(hr))
{
// Add additional metadata.
PROPVARIANT value;
value.vt = VT_LPWSTR;
value.pwszVal= L"Metadata Test Image.";
hr = piFrameQWriter->SetMetadataByName(L"/xmp/dc:title", &value);
}
クエリ式の詳細については、「 メタデータ クエリ言語の概要」を参照してください。
パート 6: エンコードされたイメージの最終処理
イメージをコピーする最後の手順は、フレームのピクセル データを書き込み、フレームをエンコーダーにコミットして、エンコーダーをコミットすることです。 エンコーダーをコミットすると、イメージ ストリームがファイルに書き込まれます。
if (SUCCEEDED(hr))
{
hr = piFrameEncode->WriteSource(
static_cast<IWICBitmapSource*> (piFrameDecode),
NULL); // Using NULL enables JPEG loss-less encoding.
}
// Commit the frame.
if (SUCCEEDED(hr))
{
hr = piFrameEncode->Commit();
}
if (piFrameDecode)
{
piFrameDecode->Release();
}
if (piFrameEncode)
{
piFrameEncode->Release();
}
if (piFrameQReader)
{
piFrameQReader->Release();
}
if (piFrameQWriter)
{
piFrameQWriter->Release();
}
}
}
if (SUCCEEDED(hr))
{
piEncoder->Commit();
}
if (SUCCEEDED(hr))
{
piFileStream->Commit(STGC_DEFAULT);
}
if (piFileStream)
{
piFileStream->Release();
}
if (piEncoder)
{
piEncoder->Release();
}
if (piBlockWriter)
{
piBlockWriter->Release();
}
if (piBlockReader)
{
piBlockReader->Release();
}
フレームの WriteSource メソッドを使用して、イメージのピクセル データを書き込みます。 これは、メタデータが書き込まれた後に行われることに注意してください。 これは、メタデータがイメージ ファイル内に十分な領域を確保するために必要です。 ピクセル データが書き込まれた後、フレームはフレームの Commit メソッドを使用してストリームに書き込まれます。 すべてのフレームが処理されると、エンコーダー (および画像) はエンコーダーの Commit メソッドを使用して最終処理されます。
フレームをコミットしたら、ループで作成された COM オブジェクトを解放する必要があります。
JPEG 再エンコードのサンプル コード
次に示すコードは、1 つの便利なブロック内のパート 1 から 6 までのコードです。
#include <Windows.h>
#include <Wincodecsdk.h>
int main()
{
// Initialize COM.
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
IWICImagingFactory *piFactory = NULL;
IWICBitmapDecoder *piDecoder = NULL;
// Create the COM imaging factory.
if (SUCCEEDED(hr))
{
hr = CoCreateInstance(CLSID_WICImagingFactory,
NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&piFactory));
}
// Create the decoder.
if (SUCCEEDED(hr))
{
hr = piFactory->CreateDecoderFromFilename(L"test.jpg", NULL, GENERIC_READ,
WICDecodeMetadataCacheOnDemand, //For JPEG lossless decoding/encoding.
&piDecoder);
}
// Variables used for encoding.
IWICStream *piFileStream = NULL;
IWICBitmapEncoder *piEncoder = NULL;
IWICMetadataBlockWriter *piBlockWriter = NULL;
IWICMetadataBlockReader *piBlockReader = NULL;
WICPixelFormatGUID pixelFormat = { 0 };
UINT count = 0;
double dpiX, dpiY = 0.0;
UINT width, height = 0;
// Create a file stream.
if (SUCCEEDED(hr))
{
hr = piFactory->CreateStream(&piFileStream);
}
// Initialize our new file stream.
if (SUCCEEDED(hr))
{
hr = piFileStream->InitializeFromFilename(L"test2.jpg", GENERIC_WRITE);
}
// Create the encoder.
if (SUCCEEDED(hr))
{
hr = piFactory->CreateEncoder(GUID_ContainerFormatJpeg, NULL, &piEncoder);
}
// Initialize the encoder
if (SUCCEEDED(hr))
{
hr = piEncoder->Initialize(piFileStream,WICBitmapEncoderNoCache);
}
if (SUCCEEDED(hr))
{
hr = piDecoder->GetFrameCount(&count);
}
if (SUCCEEDED(hr))
{
// Process each frame of the image.
for (UINT i=0; i<count && SUCCEEDED(hr); i++)
{
// Frame variables.
IWICBitmapFrameDecode *piFrameDecode = NULL;
IWICBitmapFrameEncode *piFrameEncode = NULL;
IWICMetadataQueryReader *piFrameQReader = NULL;
IWICMetadataQueryWriter *piFrameQWriter = NULL;
// Get and create the image frame.
if (SUCCEEDED(hr))
{
hr = piDecoder->GetFrame(i, &piFrameDecode);
}
if (SUCCEEDED(hr))
{
hr = piEncoder->CreateNewFrame(&piFrameEncode, NULL);
}
// Initialize the encoder.
if (SUCCEEDED(hr))
{
hr = piFrameEncode->Initialize(NULL);
}
// Get and set the size.
if (SUCCEEDED(hr))
{
hr = piFrameDecode->GetSize(&width, &height);
}
if (SUCCEEDED(hr))
{
hr = piFrameEncode->SetSize(width, height);
}
// Get and set the resolution.
if (SUCCEEDED(hr))
{
piFrameDecode->GetResolution(&dpiX, &dpiY);
}
if (SUCCEEDED(hr))
{
hr = piFrameEncode->SetResolution(dpiX, dpiY);
}
// Set the pixel format.
if (SUCCEEDED(hr))
{
piFrameDecode->GetPixelFormat(&pixelFormat);
}
if (SUCCEEDED(hr))
{
hr = piFrameEncode->SetPixelFormat(&pixelFormat);
}
// Check that the destination format and source formats are the same.
bool formatsEqual = FALSE;
if (SUCCEEDED(hr))
{
GUID srcFormat;
GUID destFormat;
hr = piDecoder->GetContainerFormat(&srcFormat);
if (SUCCEEDED(hr))
{
hr = piEncoder->GetContainerFormat(&destFormat);
}
if (SUCCEEDED(hr))
{
if (srcFormat == destFormat)
formatsEqual = true;
else
formatsEqual = false;
}
}
if (SUCCEEDED(hr) && formatsEqual)
{
// Copy metadata using metadata block reader/writer.
if (SUCCEEDED(hr))
{
piFrameDecode->QueryInterface(IID_PPV_ARGS(&piBlockReader));
}
if (SUCCEEDED(hr))
{
piFrameEncode->QueryInterface(IID_PPV_ARGS(&piBlockWriter));
}
if (SUCCEEDED(hr))
{
piBlockWriter->InitializeFromBlockReader(piBlockReader);
}
}
if(SUCCEEDED(hr))
{
hr = piFrameEncode->GetMetadataQueryWriter(&piFrameQWriter);
}
if (SUCCEEDED(hr))
{
// Add additional metadata.
PROPVARIANT value;
value.vt = VT_LPWSTR;
value.pwszVal= L"Metadata Test Image.";
hr = piFrameQWriter->SetMetadataByName(L"/xmp/dc:title", &value);
}
if (SUCCEEDED(hr))
{
hr = piFrameEncode->WriteSource(
static_cast<IWICBitmapSource*> (piFrameDecode),
NULL); // Using NULL enables JPEG loss-less encoding.
}
// Commit the frame.
if (SUCCEEDED(hr))
{
hr = piFrameEncode->Commit();
}
if (piFrameDecode)
{
piFrameDecode->Release();
}
if (piFrameEncode)
{
piFrameEncode->Release();
}
if (piFrameQReader)
{
piFrameQReader->Release();
}
if (piFrameQWriter)
{
piFrameQWriter->Release();
}
}
}
if (SUCCEEDED(hr))
{
piEncoder->Commit();
}
if (SUCCEEDED(hr))
{
piFileStream->Commit(STGC_DEFAULT);
}
if (piFileStream)
{
piFileStream->Release();
}
if (piEncoder)
{
piEncoder->Release();
}
if (piBlockWriter)
{
piBlockWriter->Release();
}
if (piBlockReader)
{
piBlockReader->Release();
}
return 0;
}
関連トピック