影像元資料

本文說明如何閱讀與撰寫影像元資料屬性,以及如何使用 GeotagHelper 工具類別來地理標籤檔案。

影像屬性

StorageFile.Properties 屬性回傳一個 StorageItemContentProperties物件,提供檔案內容相關資訊的存取。 透過呼叫 GetImagePropertiesAsync 取得影像專屬屬性。 回傳的 ImageProperties 物件會揭露包含基本影像元資料欄位的成員,例如影像標題與擷取日期。

private async void GetImageProperties(StorageFile imageFile)
{
    ImageProperties props = await imageFile.Properties.GetImagePropertiesAsync();

    string title = props.Title;
    if (title == null)
    {
        // Format does not support, or image does not contain Title property
    }

    DateTimeOffset dateTaken = props.DateTaken;
}

若要存取更多檔案元資料,請使用 Windows 屬性系統,這是一組可用唯一字串識別碼檢索的檔案元資料屬性。 建立一個字串清單,並為每個你想取得的屬性加上識別碼。 ImageProperties.RetrievePropertiesAsync 方法會將這串字串列列返回一個鍵/值對字典,其中鍵為屬性識別碼,值為屬性值。

private async void GetWindowsProperties(StorageFile imageFile)
{
    ImageProperties props = await imageFile.Properties.GetImagePropertiesAsync();

    var requests = new System.Collections.Generic.List<string>();
    requests.Add("System.Photo.Orientation");
    requests.Add("System.Photo.Aperture");

    IDictionary<string, object> retrievedProps = await props.RetrievePropertiesAsync(requests);

    ushort orientation;
    if (retrievedProps.ContainsKey("System.Photo.Orientation"))
    {
        orientation = (ushort)retrievedProps["System.Photo.Orientation"];
    }

    double aperture;
    if (retrievedProps.ContainsKey("System.Photo.Aperture"))
    {
        aperture = (double)retrievedProps["System.Photo.Aperture"];
    }
}
  • 欲了解完整的Windows屬性清單,包括每個屬性的識別碼與類型,請參見 Windows 屬性。

  • 部分屬性僅支援特定的檔案容器和影像編碼器。 關於每種影像類型支援的影像元資料清單,請參見 照片元資料政策。

  • 由於不支援的屬性在檢索時可能會回傳 null 值,因此在使用回傳的元資料值前,務必先檢查是否為 null。

地理標籤輔助工具

GeotagHelper 是一個公用程式類別,可讓您直接使用 Windows.Devices.Geolocation API,輕鬆為影像加上地理資料標記,而無需手動解析或建立中繼資料格式。

如果您已經有一個 Geopoint 物件,表示您想在影像中加上標記的位置,無論是先前使用地理位置 API 所取得,還是來自其他來源,您都可以呼叫 GeotagHelper.SetGeotagAsync,並傳入 StorageFile 和 Geopoint,以設定地理標記資料。

private async void SetGeoDataFromPoint(StorageFile imageFile)
{
    var point = new Geopoint(
        new BasicGeoposition
        {
            Latitude = 48.8567,
            Longitude = 2.3508,
        });

    await GeotagHelper.SetGeotagAsync(imageFile, point);
}

若要使用裝置目前位置設定地理標籤資料,請建立一個新的 Geolocator 物件,並呼叫 GeotagHelper.SetGeotagFromGeolocatorAsync,並輸入 Geolocator 及待標記的檔案。

private async void SetGeoDataFromGeolocator(StorageFile imageFile)
{
    var locator = new Geolocator();

    // Shows the user consent UI if needed
    var accessStatus = await Geolocator.RequestAccessAsync();
    if (accessStatus == GeolocationAccessStatus.Allowed)
    {
        await GeotagHelper.SetGeotagFromGeolocatorAsync(imageFile, locator);
    }
}

要取得代表圖片檔案地理標籤位置的 GeoPoint,請呼叫 GetGeotagAsync。

private async void GetGeoData(StorageFile imageFile)
{
    Geopoint geoPoint = await GeotagHelper.GetGeotagAsync(imageFile);
}

解碼與編碼影像元資料

處理影像資料最先進的方式,是使用 BitmapDecoder 或 BitmapEncoder 在資料流層級讀取及寫入屬性。 在這些操作中,您可以使用 Windows 屬性指定您正在讀取或寫入的資料,但您也可以使用 Windows 影像元件(WIC)提供的元資料查詢語言,指定前往所請求屬性的路徑。

使用此技術讀取影像中繼資料時,你必須具備以來源影像檔案串流建立的 BitmapDecoder。 關於如何做到這點,請參見 「建立、編輯與儲存點陣圖影像」。

一旦你有了解碼器,建立一個字串清單,並為每個你想取得的元資料屬性新增一個新條目,使用 Windows 屬性識別字串或 WIC 元資料查詢。 在解碼器的 BitmapProperties 成員上呼叫 BitmapPropertiesView.GetPropertiesAsync 方法,以要求指定的屬性。 屬性會被回傳在包含屬性名稱或路徑與屬性值的鍵值對字典中。

private async void ReadImageMetadata(BitmapDecoder bitmapDecoder)
{
    var requests = new System.Collections.Generic.List<string>();
    requests.Add("System.Photo.Orientation"); // Windows property key for EXIF orientation
    requests.Add("/xmp/dc:creator"); // WIC metadata query for Dublin Core creator

    try
    {
        var retrievedProps = await bitmapDecoder.BitmapProperties.GetPropertiesAsync(requests);

        ushort orientation;
        if (retrievedProps.ContainsKey("System.Photo.Orientation"))
        {
            orientation = (ushort)retrievedProps["System.Photo.Orientation"].Value;
        }

        string creator;
        if (retrievedProps.ContainsKey("/xmp/dc:creator"))
        {
            creator = (string)retrievedProps["/xmp/dc:creator"].Value;
        }
    }
    catch (Exception err)
    {
        switch (err.HResult)
        {
            case unchecked((int)0x88982F41): // WINCODEC_ERR_PROPERTYNOTSUPPORTED
                // The file format does not support the requested metadata.
                break;
            case unchecked((int)0x88982F81): // WINCODEC_ERR_UNSUPPORTEDOPERATION
                // The file format does not support any metadata.
            default:
                throw;
        }
    }
}
  • 關於 WIC 元資料查詢語言及其所支援屬性的資訊,請參見 WIC 影像格式的原生元資料查詢。

  • 許多元資料屬性僅由部分影像類型支援。 GetPropertiesAsync 會因錯誤代碼而失敗0x88982F41若解碼器所關聯的圖片不支援其中一個請求屬性,0x88982F81若圖片完全不支援元資料。 與這些錯誤碼相關的常數是WINCODEC_ERR_PROPERTYNOTSUPPORTED和WINCODEC_ERR_UNSUPPORTEDOPERATION,並定義在 winerror.h 標頭檔案中。

  • 由於圖片可能包含或不包含特定屬性的值,請在嘗試存取前使用 IDictionary.ContainsKey 驗證該屬性是否存在於結果中。

若要將影像中繼資料寫入串流,需要使用與影像輸出檔案相關聯的 BitmapEncoder。

建立一個 BitmapPropertySet 物件來包含你想要設定的屬性值。 建立一個 BitmapTypedValue 物件來表示屬性值。 此物件使用 object 作為值,並使用 PropertyType 列舉的成員來定義該值的型別。 將 BitmapTypedValue 加入 BitmapPropertySet ,然後呼叫 BitmapProperties.SetPropertiesAsync 讓編碼器將屬性寫入串流。

private async void WriteImageMetadata(BitmapEncoder bitmapEncoder)
{
    var propertySet = new Windows.Graphics.Imaging.BitmapPropertySet();
    var orientationValue = new Windows.Graphics.Imaging.BitmapTypedValue(
        1, // Defined as EXIF orientation = "normal"
        Windows.Foundation.PropertyType.UInt16);

    propertySet.Add("System.Photo.Orientation", orientationValue);

    try
    {
        await bitmapEncoder.BitmapProperties.SetPropertiesAsync(propertySet);
    }
    catch (Exception err)
    {
        switch (err.HResult)
        {
            case unchecked((int)0x88982F41): // WINCODEC_ERR_PROPERTYNOTSUPPORTED
                // The file format does not support this property.
                break;
            default:
                throw;
        }
    }
}