Metadados de Imagem

Este artigo mostra como ler e escrever propriedades de metadados de imagem e como geolocalizar ficheiros usando a classe utilitária GeotagHelper.

Propriedades da imagem

A propriedade StorageFile.Properties devolve um objeto StorageItemContentProperties que fornece acesso a informação relacionada com o conteúdo sobre o ficheiro. Obtenha as propriedades específicas da imagem chamando GetImagePropertiesAsync. O objeto devolvido ImageProperties expõe membros que contêm campos básicos de metadados de imagem, como o título da imagem e a data de captura.

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;
}

Para aceder a um conjunto maior de metadados de ficheiros, utilize o Windows Property System, um conjunto de propriedades de metadados de ficheiros que podem ser recuperadas com um identificador de cadeia único. Cria uma lista de cadeias e adiciona o identificador de cada propriedade que queres recuperar. O método ImageProperties.RetrievePropertiesAsync pega nesta lista de cadeias e devolve um dicionário de pares chave/valor onde a chave é o identificador de propriedade e o valor é o valor da propriedade.

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"];
    }
}
  • Para uma lista completa de Propriedades Windows, incluindo os identificadores e o tipo de cada propriedade, veja Windows Propriedades.

  • Algumas propriedades são suportadas apenas para certos contentores de ficheiros e codecs de imagem. Para uma lista dos metadados de imagem suportados para cada tipo de imagem, consulte Políticas de Metadados Fotográficos.

  • Como propriedades não suportadas podem devolver um valor nulo quando recuperadas, verifique sempre a existência de valor nulo antes de usar um valor de metadado devolvido.

Assistente de etiquetas geográficas

GeotagHelper é uma classe utilitária que facilita a identificação de imagens com dados geográficos usando o Windows. Devices.Geolocation APIs diretamente, sem necessidade de analisar ou construir manualmente o formato dos metadados.

Se já tiver um objeto Geopoint que represente a localização que pretende associar à imagem, quer de uma utilização anterior das APIs de geolocalização, quer de outra fonte, pode definir os dados de georreferenciação ao chamar GeotagHelper.SetGeotagAsync e ao passar-lhe um StorageFile e o Geopoint.

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

    await GeotagHelper.SetGeotagAsync(imageFile, point);
}

Para definir os dados de geotag usando a localização atual do dispositivo, crie um novo objeto Geolocalizator e chame GeotagHelper.SetGeotagFromGeolocatorAsync passando o Geolocator e o ficheiro a ser etiquetado.

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);
    }
}

Para obter um GeoPoint que represente a localização georreferenciada de um ficheiro de imagem, ligue para o GetGeotagAsync.

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

Decodificar e codificar metadados de imagem

A forma mais avançada de trabalhar com dados de imagem é ler e escrever as propriedades ao nível do fluxo usando um BitmapDecoder ou um BitmapEncoder. Para estas operações, pode usar as Propriedades do Windows para especificar os dados que está a ler ou escrever, mas também pode usar a linguagem de consulta de metadados fornecida pelo Windows Imaging Component (WIC) para especificar o caminho para uma propriedade solicitada.

Ler metadados de imagem usando esta técnica requer que tenha um BitmapDecoder criado com o fluxo de ficheiros de imagem de origem. Para informações sobre como fazer isto, veja Criar, editar e guardar imagens bitmap.

Depois de ter o decodificador, crie uma lista de cadeias e adicione uma nova entrada para cada propriedade de metadados que pretende recuperar, usando a cadeia de identificação de propriedades do Windows ou uma consulta de metadados WIC. Chame o método BitmapPropertiesView.GetPropertiesAsync no membro BitmapProperties do descodificador para solicitar as propriedades especificadas. As propriedades são devolvidas num dicionário de pares chave/valor, cada um contendo o nome ou o caminho da propriedade e o respetivo valor.

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;
        }
    }
}
  • Para informações sobre a linguagem de consulta de metadados WIC e as propriedades suportadas, consulte consultas nativas de metadados no formato de imagem WIC.

  • Muitas propriedades de metadados são suportadas apenas por um subconjunto de tipos de imagem. O GetPropertiesAsync falhará com o código de erro 0x88982F41 se uma das propriedades solicitadas não for suportada pela imagem associada ao descodificador e 0x88982F81 se a imagem não suportar metadados de todo. As constantes associadas a estes códigos de erro são WINCODEC_ERR_PROPERTYNOTSUPPORTED e WINCODEC_ERR_UNSUPPORTEDOPERATION e estão definidas no ficheiro de cabeçalho winerror.h.

  • Como uma imagem pode ou não conter um valor para uma determinada propriedade, use o IDictionary.ContainsKey para verificar se uma propriedade está presente nos resultados antes de tentar aceder a ela.

Escrever metadados de imagem no fluxo requer um BitmapEncoder associado ao ficheiro de saída da imagem.

Cria um objeto BitmapPropertySet para conter os valores de propriedade que queres definir. Crie um objeto BitmapTypedValue para representar o valor da propriedade. Este objeto utiliza uma enumeração objeto como valor e membro da enumeração PropertyType que define o tipo do valor. Adicione o BitmapTypedValue ao BitmapPropertySet e depois chame BitmapProperties.SetPropertiesAsync para fazer com que o codificador escreva as propriedades no fluxo.

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;
        }
    }
}