Übersicht über das Lesen und Schreiben von Bildmetadaten

Dieses Thema bietet eine Übersicht darüber, wie Sie die Windows Imaging Component(WIC)-APIs verwenden können, um Metadaten zu lesen und zu schreiben, die in Bilddateien eingebettet sind.

Dieses Thema enthält folgende Abschnitte:

Voraussetzungen

Um dieses Thema zu verstehen, sollten Sie mit dem WIC-Metadatensystem vertraut sein, wie in der WIC-Metadatenübersicht beschrieben. Sie sollten auch mit der Abfragesprache vertraut sein, die zum Lesen und Schreiben von Metadaten verwendet wird, wie unter Übersicht über die Metadatenabfragesprache beschrieben.

Einführung

WIC stellt Anwendungsentwicklern COM-Komponenten (Component Object Model) zum Lesen und Schreiben von Metadaten bereit, die in Bilddateien eingebettet sind. Es gibt zwei Möglichkeiten zum Lesen und Schreiben von Metadaten:

  • Verwenden eines Abfragelesers/Writers und eines Abfrageausdrucks zum Abfragen von Metadatenblöcken für geschachtelte Blöcke oder bestimmte Metadaten innerhalb eines Blocks.
  • Verwenden eines Metadatenhandlers (metadatenleser oder Metadaten-Writer) für den Zugriff auf die geschachtelten Metadatenblöcke oder spezifischen Metadaten innerhalb der Metadatenblöcke.

Am einfachsten ist es, einen Abfrageleser/-writer und einen Abfrageausdruck für den Zugriff auf die Metadaten zu verwenden. Ein Abfrageleser (IWICMetadataQueryReader) wird zum Lesen von Metadaten verwendet, während ein Abfragewriter (IWICMetadataQueryWriter) zum Schreiben von Metadaten verwendet wird. Beide verwenden einen Abfrageausdruck, um die gewünschten Metadaten zu lesen oder zu schreiben. Hinter den Kulissen verwendet ein Abfrageleser (und Writer) einen Metadatenhandler, um auf die Metadaten zuzugreifen, die durch den Abfrageausdruck beschrieben werden.

Die erweiterte Methode besteht darin, direkt auf die Metadatenhandler zuzugreifen. Ein Metadatenhandler wird mithilfe eines Blocklesers (IWICMetadataBlockReader) oder eines Blockwriters (IWICMetadataBlockWriter) aus den einzelnen Frames abgerufen. Die beiden verfügbaren Typen von Metadatenhandlern sind der Metadatenleser (IWICMetadataReader) und der Metadaten-Writer (IWICMetadataWriter).

Das folgende Diagramm des Inhalts einer JPEG-Bilddatei wird in den Beispielen in diesem Thema verwendet. Das in diesem Diagramm dargestellte Bild wurde mithilfe von Microsoft Paint erstellt. Die Bewertungsmetadaten wurden mithilfe der Fotogalerie-Funktion von Windows Vista hinzugefügt.

Abbildung des JPEG-Bilds mit Bewertungsmetadaten

Lesen von Metadadaten mithilfe eines Abfragelesers

Die einfachste Möglichkeit zum Lesen von Metadaten ist die Verwendung der Abfrageleseschnittstelle IWICMetadataQueryReader. Mit dem Abfrageleser können Sie Metadatenblöcke und Elemente in Metadatenblöcken mithilfe eines Abfrageausdrucks lesen.

Es gibt drei Möglichkeiten, einen Abfrageleser zu erhalten: über einen Bitmapdecoder (IWICBitmapDecoder), über seine einzelnen Frames (IWICBitmapFrameDecode) oder über einen Abfragewriter (IWICMetadataQueryWriter).

Abrufen eines Abfragelesers

Der folgende Beispielcode zeigt, wie Sie einen Bitmapdecoder aus der Imaging Factory abrufen und einen einzelnen Bitmapframe abrufen. Dieser Code führt auch Setuparbeiten aus, die erforderlich sind, um einen Abfrageleser aus einem decodierten Frame zu erhalten.

IWICImagingFactory *pFactory = NULL;
IWICBitmapDecoder *pDecoder = NULL;
IWICBitmapFrameDecode *pFrameDecode = NULL;
IWICMetadataQueryReader *pQueryReader = NULL;
IWICMetadataQueryReader *pEmbedReader = NULL;
PROPVARIANT value;

// Initialize COM
CoInitialize(NULL);

// Initialize PROPVARIANT
PropVariantInit(&value);

//Create the COM imaging factory
HRESULT hr = CoCreateInstance(
    CLSID_WICImagingFactory,
    NULL,
    CLSCTX_INPROC_SERVER,
    IID_IWICImagingFactory,
    (LPVOID*)&pFactory);

// Create the decoder
if (SUCCEEDED(hr))
{
    hr = pFactory->CreateDecoderFromFilename(
        L"test.jpg",
        NULL,
        GENERIC_READ,
        WICDecodeMetadataCacheOnDemand,
        &pDecoder);
}

// Get a single frame from the image
if (SUCCEEDED(hr))
{
    hr = pDecoder->GetFrame(
         0,  //JPEG has only one frame.
         &pFrameDecode); 
}

Der Bitmapdecoder für die test.jpg-Datei wird mithilfe der CreateDecoderFromFilename-Methode der Imaging Factory abgerufen. In dieser Methode wird der vierte Parameter auf den Wert WICDecodeMetadataCacheOnDemand aus der WICDecodeOptions-Enumeration festgelegt. Dies weist den Decoder an, die Metadaten zwischenzuspeichern, wenn die Metadaten benötigt werden. entweder durch Abrufen eines Abfragelesers oder des zugrunde liegenden Metadatenlesers. Mit dieser Option können Sie den Datenstrom in den Metadaten beibehalten, die für die schnelle Metadatencodierung erforderlich sind, und ermöglicht die verlustfreie Decodierung des JPEG-Bilds. Alternativ können Sie den anderen WICDecodeOptions-Wert WICDecodeMetadataCacheOnLoad verwenden, der die eingebetteten Bildmetadaten zwischenspeichert, sobald das Bild geladen wird.

Um den Abfrageleser des Frames zu erhalten, rufen Sie einfach die GetMetadataQueryReader-Methode des Frames auf. Der folgende Code veranschaulicht diesen Aufruf.

// Get the query reader
if (SUCCEEDED(hr))
{
    hr = pFrameDecode->GetMetadataQueryReader(&pQueryReader);
}

Ebenso kann ein Abfrageleser auch auf Decoderebene abgerufen werden. Ein einfacher Aufruf der GetMetadataQueryReader-Methode des Decoders ruft den Abfrageleser des Decoders ab. Der Abfrageleser eines Decoders liest im Gegensatz zum Abfrageleser eines Frames Metadaten für ein Bild, das sich außerhalb der einzelnen Frames befindet. Dieses Szenario ist jedoch nicht üblich, und die nativen Bildformate unterstützen diese Funktion nicht. Die von WIC bereitgestellten nativen Bildcodecs lesen und schreiben Metadaten auf Frameebene, auch für Singleframeformate wie JPEG.

Lesen von Metadaten

Bevor Sie mit dem Tatsächlichen Lesen von Metadaten fortfahren, sehen Sie sich das folgende Diagramm einer JPEG-Datei an, die eingebettete Metadatenblöcke und die abzurufenden tatsächlichen Daten enthält. Dieses Diagramm enthält Beschriftungen für bestimmte Metadatenblöcke und Elemente innerhalb des Bilds, die den Metadatenabfrageausdruck für jeden Block oder jedes Element bereitstellen.

Abbildung eines JPEG-Bilds mit Metadaten-Beschriftungen

Rufen Sie die GetMetadataByName-Methode auf, um eingebettete Metadatenblöcke oder bestimmte Elemente anhand des Namens abzufragen. Diese Methode verwendet einen Abfrageausdruck und einen PROPVARIANT , in dem das Metadatenelement zurückgegeben wird. Der folgende Code fragt einen geschachtelten Metadatenblock ab und konvertiert die vom PROPVARIANT-Wert bereitgestellte IUnknown-Komponente in einen Abfrageleser, falls gefunden.

if (SUCCEEDED(hr))
{
    // Get the nested IFD reader
    hr = pQueryReader->GetMetadataByName(L"/app1/ifd", &value);
    if (value.vt == VT_UNKNOWN)
    {
        hr = value.punkVal->QueryInterface(IID_IWICMetadataQueryReader, (void **)&pEmbedReader);
    }
    PropVariantClear(&value); // Clear value for new query
}

Der Abfrageausdruck "/app1/ifd" fragt den IFD-Block ab, der im App1-Block geschachtelt ist. Die JPEG-Bilddatei enthält den GESCHACHTELTEN METADATENblock IFD, sodass PROPVARIANT mit einem Variablentyp (vt) von VT_UNKNOWN und einem Zeiger auf eine IUnknown-Schnittstelle (punkVal) zurückgegeben wird. Anschließend fragen Sie die IUnknown-Schnittstelle nach einem Abfrageleser ab.

Der folgende Code veranschaulicht eine neue Abfrage basierend auf dem neuen Abfrageleser relativ zum geschachtelten IFD-Block.

if (SUCCEEDED(hr))
{
    hr = pEmbedReader->GetMetadataByName(L"/{ushort=18249}", &value);
    PropVariantClear(&value); // Clear value for new query
}

Der Abfrageausdruck "/{ushort=18249}" fragt den IFD-Block nach der MicrosoftPhoto-Bewertung ab, die unter tag 18249 eingebettet ist. Der PROPVARIANT-Wert enthält nun den Werttyp VT_UI2 und den Datenwert 50.

Es ist jedoch nicht erforderlich, einen geschachtelten Block abzurufen, bevor bestimmte Datenwerte abgefragt werden. Für instance können Sie anstelle von Abfragen nach der geschachtelten IFD und dann nach der MicrosoftPhoto-Bewertung stattdessen den Stammmetadatenblock und die abfrage im folgenden Code verwenden, um die gleichen Informationen abzurufen.

if (SUCCEEDED(hr))
{
    hr = pQueryReader->GetMetadataByName(L"/app1/ifd/{ushort=18249}", &value);
    PropVariantClear(&value);
}

Zusätzlich zum Abfragen bestimmter Metadatenelemente in einem Metadatenblock können Sie auch alle Metadatenelemente in einem Metadatenblock aufzählen (ohne Metadatenelemente in geschachtelte Metadatenblöcke). Zum Aufzählen der Metadatenelemente im aktuellen Block wird die GetEnumeration-Methode des Abfragelesers verwendet. Diese Methode ruft eine IEnumString-Schnittstelle ab, die mit den Metadatenelementen im aktuellen Block aufgefüllt wird. Der folgende Code listet beispielsweise die XMP-Bewertung und die MicrosoftPhoto-Bewertung für den geschachtelten IFD-Block auf, der zuvor abgerufen wurde.

IEnumString *metadataItems = NULL;

if (SUCCEEDED(hr))
{
    hr = pEmbedReader->GetEnumerator(&metadataItems);
}

Weitere Informationen zum Identifizieren geeigneter Tags für verschiedene Bildformate und Metadatenformate finden Sie unter Metadatenabfragen für native Bildformate.

Zusätzliche Abfragelesemethoden

Zusätzlich zum Lesen von Metadaten können Sie auch zusätzliche Informationen über den Abfrageleser erhalten und Metadaten auf andere Weise abrufen. Der Abfrageleser stellt zwei Methoden bereit, die Informationen über den Abfrageleser bereitstellen: GetContainerFormat und GetLocation.

Mit dem eingebetteten Abfrageleser können Sie GetContainerFormat verwenden, um den Typ des Metadatenblocks zu bestimmen, und Sie können GetLocation aufrufen, um den Pfad relativ zum Stammmetadatenblock abzurufen. Der folgende Code fragt den eingebetteten Abfrageleser nach seinem Speicherort ab.

// Determine the metadata block format

if (SUCCEEDED(hr))
{
    hr = pEmbedReader->GetContainerFormat(&containerGUID);
}

// Determine the query reader's location
if (SUCCEEDED(hr))
{
    UINT length;
    WCHAR readerNamespace[100];
    hr = pEmbedReader->GetLocation(100, readerNamespace, &length);
}

Der Aufruf von GetContainerFormat für den eingebetteten Abfrageleser gibt die IFD-Metadatenformat-GUID zurück. Der Aufruf von GetLocation gibt den Namespace "/app1/ifd" zurück. Sie erhalten den relativen Pfad, von dem aus nachfolgende Abfragen an den neuen Abfrageleser ausgeführt werden. Natürlich ist der obige Code nicht sehr nützlich, aber er veranschaulicht, wie die GetLocation-Methode zum Suchen geschachtelter Metadatenblöcke verwendet wird.

Schreiben von Metadaten mithilfe eines Abfrage-Writers

Hinweis

Einige der in diesem Abschnitt bereitgestellten Codebeispiele werden nicht im Kontext der eigentlichen Schritte angezeigt, die zum Schreiben von Metadaten erforderlich sind. Informationen zum Anzeigen der Codebeispiele im Kontext eines funktionierenden Beispiels finden Sie im Tutorial Vorgehensweise: Erneutes Codieren eines Bilds mit Metadaten.

 

Die Standard Komponente zum Schreiben von Metadaten ist der Abfragewriter (IWICMetadataQueryWriter). Mit dem Abfrage-Writer können Sie Metadatenblöcke und Elemente innerhalb eines Metadatenblocks festlegen und entfernen.

Wie der Abfrageleser gibt es drei Möglichkeiten, einen Abfrage-Writer zu erhalten: über einen Bitmapencoder (IWICBitmapEncoder), durch seine einzelnen Frames (IWICBitmapFrameEncode) oder über einen schnellen Metadatenencoder (IWICFastMetadataEncoder).

Abrufen eines Abfrage-Writers

Der häufigste Abfrage-Writer ist für einen einzelnen Frame einer Bitmap. Dieser Abfrage-Writer legt die Metadatenblöcke und Elemente eines Bildframes fest und entfernt sie. Um den Abfragewriter eines Bildframes zu erhalten, rufen Sie die GetMetadataQueryWriter-Methode des Frames auf. Der folgende Code veranschaulicht den einfachen Methodenaufruf zum Abrufen des Abfrage-Writers eines Frames.

IWICMetadataQueryWriter &pFrameQWriter = NULL;

//Obtain a query writer from the frame.
hr = pFrameEncode->GetMetadataQueryWriter(&pFrameQWriter);

Auf ähnliche Weise kann auch ein Abfragewriter für die Encoderebene abgerufen werden. Ein einfacher Aufruf der GetMetadataQueryWriter-Methode des Encoders ruft den Abfragewriter des Encoders ab. Der Abfrageschreiber eines Encoders schreibt im Gegensatz zum Abfrage-Writer eines Frames Metadaten für ein Bild außerhalb des einzelnen Frames. Dieses Szenario ist jedoch nicht üblich, und die nativen Bildformate unterstützen diese Funktion nicht. Die von WIC bereitgestellten nativen Bildcodecs zum Lesen und Schreiben von Metadaten auf Frameebene auch für Einzelbildformate wie JPEG.

Sie können auch einen Abfrageschreiber direkt aus der Imaging Factory (IWICImagingFactory) abrufen. Es gibt zwei Imaging Factory-Methoden, die einen Abfrageschreiber zurückgeben: CreateQueryWriter und CreateQueryWriterFromReader.

CreateQueryWriter erstellt einen Abfrageschreiber für das angegebene Metadatenformat und den angegebenen Anbieter. Mit diesem Abfrageschreiber können Sie Metadaten für ein bestimmtes Metadatenformat schreiben und dem Bild hinzufügen. Der folgende Code veranschaulicht einen CreateQueryWriter-Aufruf zum Erstellen eines XMP-Abfrage-Writers.

IWICMetadataQueryWriter *pXMPWriter = NULL;

// Create XMP block
GUID vendor = GUID_VendorMicrosoft;
hr = pFactory->CreateQueryWriter(
        GUID_MetadataFormatXMP,
        &vendor,
        &pXMPWriter);

In diesem Beispiel wird der Anzeigename GUID_MetadataFormatXMP als guidMetadataFormat-Parameter verwendet. Es stellt die GUID im XMP-Metadatenformat dar, und der Anbieter stellt den von Microsoft erstellten Handler dar. Alternativ kann NULL als pguidVendor-Parameter mit den gleichen Ergebnissen übergeben werden, wenn kein anderer XMP-Handler vorhanden ist. Wenn ein benutzerdefinierter XMP-Handler neben dem nativen XMP-Handler installiert ist, führt die Übergabe von NULL für den Anbieter dazu, dass der Abfrageschreiber mit der niedrigsten GUID zurückgegeben wird.

CreateQueryWriterFromReader ähnelt der CreateQueryWriter-Methode , mit der Ausnahme, dass sie den neuen Abfrageschreiber mit den vom Abfrageleser bereitgestellten Daten vorfüllt. Dies ist nützlich, um ein Image neu zu codieren, während die vorhandenen Metadaten verwaltet werden, oder um unerwünschte Metadaten zu entfernen. Der folgende Code veranschaulicht einen CreateQueryWriterFromReader-Aufruf .

hr = pFrameDecode->GetMetadataQueryReader(&pFrameQReader);

// Copy metadata using query readers
if(SUCCEEDED(hr) && pFrameQReader)
{
    IWICMetadataQueryWriter *pNewWriter = NULL;

    GUID vendor = GUID_VendorMicrosoft;
    hr = pFactory->CreateQueryWriterFromReader(
        pFrameQReader,
        &vendor,
        &pNewWriter);

Hinzufügen von Metadaten

Nachdem Sie einen Abfrage-Writer erhalten haben, können Sie ihn verwenden, um Metadatenblöcke und -elemente hinzuzufügen. Zum Schreiben von Metadaten verwenden Sie die SetMetadataByName-Methode des Abfrageschreibers. SetMetadataByName akzeptiert zwei Parameter: einen Abfrageausdruck (wzName) und einen Zeiger auf einen PROPVARIANT (pvarValue). Der Abfrageausdruck definiert den festzulegenden Block oder Das Element, während PROPVARIANT den tatsächlich festzulegenden Datenwert bereitstellt.

Im folgenden Beispiel wird veranschaulicht, wie Sie einen Titel mithilfe des XMP-Abfrageschreibers hinzufügen, der zuvor mit der CreateQueryWriter-Methode abgerufen wurde.

// Write metadata to the XMP writer
if (SUCCEEDED(hr))
{
    PROPVARIANT value;
    PropVariantInit(&value);

    value.vt = VT_LPWSTR;
    value.pwszVal = L"Metadata Test Image.";
   
    hr = pXMPWriter->SetMetadataByName(L"/dc:title", &value);

    PropVariantClear(&value);
}

In diesem Beispiel wird der Werttyp (vt) auf VT_LPWSTRfestgelegt, was angibt, dass eine Zeichenfolge als Datenwert verwendet wird. Da der Werttyp eine Zeichenfolge ist, wird pwszVal verwendet, um den zu verwendenden Titel festzulegen. SetMetadataByName wird dann mit dem Abfrageausdruck "/dc:title" und dem neu festgelegten PROPVARIANT aufgerufen. Der verwendete Abfrageausdruck gibt an, dass die title-Eigenschaft im Dc-Schema (Digital Camera) festgelegt werden soll. Beachten Sie, dass der Ausdruck nicht "/xmp/dc:title" ist. Dies liegt daran, dass der Abfrageschreiber bereits XMP-spezifisch ist und keinen eingebetteten XMP-Block enthält, was "/xmp/dc:title" vorschlagen würde.

Bis zu diesem Punkt haben Sie keine Metadaten zu einem Bildrahmen hinzugefügt. Sie haben einfach einen Abfrageschreiber mit Daten aufgefüllt. Um einem Frame einen Metadatenblock hinzuzufügen, der vom Abfrageschreiber dargestellt wird, rufen Sie erneut SetMetadataByName auf, indem Sie den Abfrageschreiber als Wert von PROPVARIANT verwenden. Dadurch werden die Metadaten im Abfrageschreiber effektiv in den Bildrahmen kopiert. Der folgende Code veranschaulicht, wie Sie die Metadaten im XMP-Abfrageschreiber hinzufügen, der zuvor dem Stammmetadatenblock eines Frames abgerufen wurde.

// Get the frame's query writer and write the XMP query writer to it
if (SUCCEEDED(hr))
{
    hr = pFrameEncode->GetMetadataQueryWriter(&pFrameQWriter);

    // Copy the metadata in the XMP query writer to the frame
    if (SUCCEEDED(hr))
    {
        PROPVARIANT value;

        PropVariantInit(&value);
        value.vt = VT_UNKNOWN;
        value.punkVal = pXMPWriter;
        value.punkVal->AddRef();

        hr = pFrameQWriter->SetMetadataByName(L"/", &value);

        PropVariantClear(&value);
    }
}

In diesem Beispiel wird ein Werttyp (vt) von verwendet, der VT_UNKOWN einen COM-Schnittstellenwerttyp angibt. Der XMP-Abfrageschreiber (piXMPWriter) wird dann als Wert von PROPVARIANT verwendet, indem mithilfe der AddRef-Methode ein Verweis darauf hinzugefügt wird. Schließlich wird der XMP-Abfrageschreiber festgelegt, indem die SetMetadataByName-Methode des Frames aufgerufen und der Abfrageausdruck "/" übergeben wird, der den Stammblock angibt, und das neu festgelegte PROPVARIANT.

Hinweis

Wenn der Frame bereits den Metadatenblock enthält, den Sie hinzufügen möchten, werden die metadaten, die Sie hinzufügen, hinzugefügt und vorhandene Metadaten überschrieben.

 

Entfernen von Metadaten

Mit einem Abfrageschreiber können Sie auch Metadaten entfernen, indem Sie die RemoveMetadataByName-Methode aufrufen. RemoveMetadataByName nimmt einen Abfrageausdruck an und entfernt den Metadatenblock oder das Metadatenelement, sofern vorhanden. Der folgende Code veranschaulicht, wie Sie den titel entfernen, der zuvor hinzugefügt wurde.

if (SUCCEEDED(hr))
{
    hr = pFrameQWriter->RemoveMetadataByName(L"/xmp/dc:title");
}

Der folgende Code veranschaulicht, wie der gesamte XMP-Metadatenblock entfernt wird.

if (SUCCEEDED(hr))
{
    hr = pFrameQWriter->RemoveMetadataByName(L"/xmp");
}

Kopieren von Metadaten für die Neucodierung

Hinweis

Der Code in diesem Abschnitt ist nur gültig, wenn die Quell- und Zielimageformate identisch sind. Beim Codieren in einem anderen Bildformat können Sie nicht alle Metadaten eines Bilds in einem einzigen Vorgang kopieren.

 

Zum Beibehalten von Metadaten beim Erneuten Codieren eines Bilds in dasselbe Bildformat stehen Methoden zum Kopieren aller Metadaten in einem einzigen Vorgang zur Verfügung. Jeder dieser Vorgänge folgt einem ähnlichen Muster. jede legt die Metadaten des decodierten Frames direkt in den neuen Frame fest, der codiert wird.

Die bevorzugte Methode zum Kopieren von Metadaten besteht darin, den Blockschreiber des neuen Frames mit dem Blockleser des decodierten Frames zu initialisieren. Der folgende Code veranschaulicht diese Methode.

if (SUCCEEDED(hr) && formatsEqual)
{
    // Copy metadata using metadata block reader/writer
    if (SUCCEEDED(hr))
    {
        pFrameDecode->QueryInterface(
            IID_IWICMetadataBlockReader,
            (void**)&pBlockReader);
    }
    if (SUCCEEDED(hr))
    {
        pFrameEncode->QueryInterface(
            IID_IWICMetadataBlockWriter,
            (void**)&pBlockWriter);
    }
    if (SUCCEEDED(hr))
    {
        pBlockWriter->InitializeFromBlockReader(pBlockReader);
    }
}

In diesem Beispiel werden der Blockleser und der Blockschreiber aus dem Quellframe bzw. zielframe abgerufen. Der Blockschreiber wird dann vom Blockleser initialisiert. Dadurch wird der Blockleser mit den vorab aufgefüllten Metadaten des Blocklesers initialisiert.

Eine weitere Methode zum Kopieren von Metadaten ist das Schreiben des Metadatenblocks, auf den der Abfrageleser verweist, mithilfe des Abfrageschreibers des Encoders. Der folgende Code veranschaulicht diese Methode.

if (SUCCEEDED(hr) && formatsEqual)
{
    hr = pFrameDecode->GetMetadataQueryReader(&pFrameQReader);

    // Copy metadata using query readers
    if(SUCCEEDED(hr))
    {
        hr = pFrameEncode->GetMetadataQueryWriter(&pFrameQWriter);
        if (SUCCEEDED(hr))
        {
            PropVariantClear(&value);
            value.vt=VT_UNKNOWN;
            value.punkVal=pFrameQReader;
            value.punkVal->AddRef();
            hr = pFrameQWriter->SetMetadataByName(L"/", &value);
            PropVariantClear(&value);
        }
    }
}

Hier wird ein Abfrageleser aus dem decodierten Frame abgerufen und dann als Eigenschaftswert der PROPVARIANT verwendet, wobei ein Werttyp auf VT_UNKNOWN festgelegt ist. Der Abfrageschreiber für den Encoder wird abgerufen, und der Abfrageausdruck "/" wird verwendet, um die Metadaten im Stammnavigationspfad festzulegen. Sie können diese Methode auch beim Festlegen geschachtelter Metadatenblöcke verwenden, indem Sie den Abfrageausdruck an den gewünschten Speicherort anpassen.

Auf ähnliche Weise können Sie einen Abfrageschreiber basierend auf dem Abfrageleser des decodierten Frames erstellen, indem Sie die CreateQueryWriterFromReader-Methode der Imaging Factory verwenden. Der in diesem Vorgang erstellte Abfrageschreiber wird mit den Metadaten aus dem Abfrageleser vorgefüllt und kann dann im Frame festgelegt werden. Der folgende Code veranschaulicht den Kopiervorgang CreateQueryWriterFromReader .

IWICMetadataQueryWriter *pNewWriter = NULL;

GUID vendor = GUID_VendorMicrosoft;
hr = pFactory->CreateQueryWriterFromReader(
    pFrameQReader,
    &vendor,
    &pNewWriter);

if (SUCCEEDED(hr))
{
    // Get the frame's query writer
    hr = pFrameEncode->GetMetadataQueryWriter(&pFrameQWriter);
}

// Set the query writer to the frame.
if (SUCCEEDED(hr))
{
    PROPVARIANT value;

    PropVariantInit(&value);
    value.vt = VT_UNKNOWN;
    value.punkVal = pNewWriter;
    value.punkVal->AddRef();
    hr = pFrameQWriter->SetMetadataByName(L"/",&value);
}

Diese Methode verwendet einen separaten Abfrageschreiber, der auf den Daten des Abfragelesers basiert. Dieser neue Abfrageschreiber wird dann im Frame festgelegt.

Auch diese Vorgänge zum Kopieren von Metadaten funktionieren nur, wenn die Quell- und Zielimages das gleiche Format haben. Dies liegt daran, dass unterschiedliche Bildformate die Metadatenblöcke an unterschiedlichen Speicherorten speichern. Für instance unterstützen sowohl JPEG als auch TIFF XMP-Metadatenblöcke. In JPEG-Bildern befindet sich der XMP-Block im Stammmetadatenblock, wie in der WIC-Metadatenübersicht veranschaulicht. In einem TIFF-Image ist der XMP-Block jedoch in einem IFD-Stammblock geschachtelt. Das folgende Diagramm veranschaulicht die Unterschiede zwischen einem JPEG-Bild und einem TIFF-Bild mit den gleichen Bewertungsmetadaten.

JPEG- und tiff-Vergleich.

Schnelle Metadatencodierung

Es ist nicht immer erforderlich, ein Bild neu zu codieren, um neue Metadaten in es zu schreiben. Metadaten können auch mithilfe eines schnellen Metadatenencoders geschrieben werden. Ein schneller Metadatenencoder kann eine begrenzte Menge von Metadaten in ein Bild schreiben, ohne das Bild neu zu codieren. Dies wird erreicht, indem die neuen Metadaten in den leeren Abstand geschrieben werden, der von einigen Metadatenformaten bereitgestellt wird. Die nativen Metadatenformate, die die Metadatenfüllung unterstützen, sind Exif, IFD, GPS und XMP.

Hinzufügen von Padding zu Metadatenblöcken

Bevor Sie eine schnelle Metadatencodierung durchführen können, muss im Metadatenblock Platz zum Schreiben weiterer Metadaten vorhanden sein. Wenn in der vorhandenen Auffüllung nicht genügend Platz zum Schreiben der neuen Metadaten vorhanden ist, schlägt die schnelle Metadatencodierung fehl. Um einem Bild Metadaten-Auffüllung hinzuzufügen, muss das Image neu codiert werden. Sie können das Auffüllen auf die gleiche Weise hinzufügen, wie Sie jedes andere Metadatenelement mithilfe eines Abfrageausdrucks hinzufügen würden, wenn der Metadatenblock, den Sie auffüllen, dies unterstützt. Im folgenden Beispiel wird veranschaulicht, wie sie einem in einem App1-Block eingebetteten IFD-Block eine Auffüllung hinzufügen.

if (SUCCEEDED(hr))
{
    // Add metadata padding
    PROPVARIANT padding;

    PropVariantInit(&padding);
    padding.vt = VT_UI4;
    padding.uiVal = 4096; // 4KB

    hr = pFrameQWriter->SetMetadataByName(L"/app1/ifd/PaddingSchema:padding", &padding);

    PropVariantClear(&padding);
}

Erstellen Sie zum Hinzufügen des Auffüllens eine PROPVARIANT vom Typ VT_UI4 und einen Wert, der der Anzahl der Hinzuzufügenden Bytes entspricht. Ein typischer Wert ist 4096 Bytes. Die Metadatenabfragen für JPEG, TIFF und JPEG-XR befinden sich in dieser Tabelle.

Metadatenformat JPEG-Metadatenabfrage TIFF, JPEG-XR-Metadatenabfrage
IFD /app1/ifd/PaddingSchema:Padding /ifd/PaddingSchema:Padding
EXIF /app1/ifd/exif/PaddingSchema:Padding /ifd/exif/PaddingSchema:Padding
XMP /xmp/PaddingSchema:Padding /ifd/xmp/PaddingSchema:Padding
GPS /app1/ifd/gps/PaddingSchema:Padding /ifd/gps/PaddingSchema:Padding

 

Abrufen eines schnellen Metadatenencoders

Wenn Sie über ein Bild mit Metadatenabstand verfügen, kann ein schneller Metadatenencoder mithilfe der Imaging Factorymethoden CreateFastMetadataEncoderFromDecoder und CreateFastMetadataEncoderFromFrameDecoder abgerufen werden.

Wie der Name schon sagt, erstellt CreateFastMetadataEncoderFromDecoder einen schnellen Metadatenencoder für Metadaten auf Decoderebene. Die von WIC bereitgestellten nativen Bildformate unterstützen keine Metadaten auf Decoderebene, aber diese Methode wird für den Fall bereitgestellt, dass ein solches Bildformat in Zukunft entwickelt wird.

Das gängigere Szenario besteht darin, mithilfe von CreateFastMetadataEncoderFromFrameDecode einen schnellen Metadatenencoder aus einem Bildframe zu erhalten. Der folgende Code ruft den schnellen Metadatenencoder eines decodierten Frames ab und ändert den Bewertungswert im App1-Block.

if (SUCCEEDED(hr))
{
    IWICFastMetadataEncoder *pFME = NULL;
    IWICMetadataQueryWriter *pFMEQW = NULL;

    hr = pFactory->CreateFastMetadataEncoderFromFrameDecode(
        pFrameDecode, 
        &pFME);
}

Verwenden des schnellen Metadatenencoders

Über den schnellen Metadatenencoder können Sie einen Abfrage-Writer abrufen. Dadurch können Sie Metadaten mithilfe eines Abfrageausdrucks schreiben, wie zuvor gezeigt. Nachdem Metadaten im Abfrage-Writer festgelegt wurden, committen Sie den schnellen Metadatenencoder, um die Metadatenaktualisierung abzuschließen. Der folgende Code veranschaulicht das Festlegen und Committen der Metadatenänderungen.

    if (SUCCEEDED(hr))
    {
        hr = pFME->GetMetadataQueryWriter(&pFMEQW);
    }

    if (SUCCEEDED(hr))
    {
        // Add additional metadata
        PROPVARIANT value;

        PropVariantInit(&value);

        value.vt = VT_UI4;
        value.uiVal = 99;
        hr = pFMEQW->SetMetadataByName(L"/app1/ifd/{ushort=18249}", &value);

        PropVariantClear(&value);
    }

    if (SUCCEEDED(hr))
    {
        hr = pFME->Commit();
    }
}

Wenn commit aus irgendeinem Grund fehlschlägt, müssen Sie das Image neu codieren, um sicherzustellen, dass dem Image die neuen Metadaten hinzugefügt werden.

Konzept

Übersicht über die Windows-Imageerstellungskomponente

Übersicht über WIC-Metadaten

Übersicht über die Metadaten-Abfragesprache

Übersicht über die Metadatenerweiterbarkeit

Vorgehensweise: Erneutes Codieren eines JPEG-Bilds mit Metadaten