it-swarm-ru.tech

Получение данных GPS из EXIF ​​изображения в C #

Я разрабатываю систему, которая позволяет загружать изображение на сервер с помощью ASP.NET C #. Я обрабатываю изображение, и все работает отлично. Мне удалось найти метод, который считывает данные EXIF ​​Date Created и анализирует его как DateTime. Это тоже прекрасно работает.

Я сейчас пытаюсь прочитать данные GPS из EXIF. Я хочу захватить цифры широты и долготы.

Я использую этот список как ссылку на данные EXIF ​​(используя числа для элементов свойств) http://www.exiv2.org/tags.html

Вот метод, чтобы захватить дату создания (который работает).

public DateTime GetDateTaken(Image targetImg)
{
    DateTime dtaken;

    try
    {
        //Property Item 306 corresponds to the Date Taken
        PropertyItem propItem = targetImg.GetPropertyItem(0x0132);

        //Convert date taken metadata to a DateTime object
        string sdate = Encoding.UTF8.GetString(propItem.Value).Trim();
        string secondhalf = sdate.Substring(sdate.IndexOf(" "), (sdate.Length - sdate.IndexOf(" ")));
        string firsthalf = sdate.Substring(0, 10);
        firsthalf = firsthalf.Replace(":", "-");
        sdate = firsthalf + secondhalf;
        dtaken = DateTime.Parse(sdate);
    }
    catch
    {
        dtaken = DateTime.Parse("1956-01-01 00:00:00.000");
    }
    return dtaken;
}

Ниже моя попытка сделать то же самое для GPS ..

public float GetLatitude(Image targetImg)
{
    float dtaken;

    try
    {
        //Property Item 0x0002 corresponds to the Date Taken
        PropertyItem propItem = targetImg.GetPropertyItem(2);

        //Convert date taken metadata to a DateTime object
        string sdate = Encoding.UTF8.GetString(propItem.Value).Trim();
        dtaken = float.Parse(sdate);
    }
    catch
    {
        dtaken = 0;
    }
    return dtaken;
}

Значение, которое выходит в sdate: "5\0\0\0\0\0\0l\t\0\0d\0\0\0\0\0\0\0\0\0\0"

И это происходит из-за изображения, сделанного iPhone 4, на котором есть данные GPS EXIF.

Я знаю, что есть классы, которые делают это, но я бы предпочел написать свой - хотя я открыт для предложений :-)

Заранее спасибо.

25
tmutton

Согласно ссылке, размещенной выше tomfanning, элемент свойства 0x0002 - это широта, выраженная как PropertyTagTypeRational. Рациональный тип определяется как ...

Указывает, что элемент данных значения представляет собой массив пар длинных целых без знака. Каждая пара представляет дробь; первое целое число является числителем, а второе целое является знаменателем.

Вы пытаетесь разобрать его как строку, когда на самом деле это просто последовательность байтов. В соответствии с вышеизложенным, в этот байтовый массив должно быть 3 пары 32-разрядных целых чисел без знака, которые можно получить с помощью следующего:

uint degreesNumerator   = BitConverter.ToUInt32(propItem.Value, 0);
uint degreesDenominator = BitConverter.ToUInt32(propItem.Value, 4);
uint minutesNumerator   = BitConverter.ToUInt32(propItem.Value, 8);
uint minutesDenominator = BitConverter.ToUInt32(propItem.Value, 12);
uint secondsNumerator   = BitConverter.ToUInt32(propItem.Value, 16);
uint secondsDenominator = BitConverter.ToUInt32(propItem.Value, 20);

То, что вы делаете с этими значениями после того, как вы их получили, это для вас, чтобы работать :) Вот что говорят документы:

Широта выражается в виде трех рациональных значений, дающих соответственно градусы, минуты и секунды. Когда выражены градусы, минуты и секунды, формат будет дд/1, мм/1, сс/1. Когда используются градусы и минуты и, например, доли минут задаются с точностью до двух десятичных знаков, форматом является дд/1, мммм/100, 0/1.

24
Jon Grant

Простой (и быстрый!) Способ - использовать мою библиотеку с открытым исходным кодом MetadataExtractor:

var gps = ImageMetadataReader.ReadMetadata(path)
                             .OfType<GpsDirectory>()
                             .FirstOrDefault();

var location = gps.GetGeoLocation();

Console.WriteLine("Image at {0},{1}", location.Latitude, location.Longitude);

Библиотека написана на чистом C # и поддерживает множество форматов изображений и декодирует данные, характерные для многих моделей камер.

Это доступно через NuGet или GitHub .

14
Drew Noakes

Я наткнулся на это в поисках способа получить данные EXIF ​​GPS в виде набора чисел с плавающей точкой. Я адаптировал код от Джона Гранта следующим образом ...

public static float? GetLatitude(Image targetImg)
{
    try
    {
        //Property Item 0x0001 - PropertyTagGpsLatitudeRef
        PropertyItem propItemRef = targetImg.GetPropertyItem(1);
        //Property Item 0x0002 - PropertyTagGpsLatitude
        PropertyItem propItemLat = targetImg.GetPropertyItem(2);
        return ExifGpsToFloat(propItemRef, propItemLat);
    }
    catch (ArgumentException)
    {
        return null;
    }
}
public static float? GetLongitude(Image targetImg)
{
    try
    {
        ///Property Item 0x0003 - PropertyTagGpsLongitudeRef
        PropertyItem propItemRef = targetImg.GetPropertyItem(3);
        //Property Item 0x0004 - PropertyTagGpsLongitude
        PropertyItem propItemLong = targetImg.GetPropertyItem(4);
        return ExifGpsToFloat(propItemRef, propItemLong);
    }
    catch (ArgumentException)
    {
        return null;
    }
}
private static float ExifGpsToFloat(PropertyItem propItemRef, PropertyItem propItem)
{
    uint degreesNumerator = BitConverter.ToUInt32(propItem.Value, 0);
    uint degreesDenominator = BitConverter.ToUInt32(propItem.Value, 4);
    float degrees = degreesNumerator / (float)degreesDenominator;

    uint minutesNumerator = BitConverter.ToUInt32(propItem.Value, 8);
    uint minutesDenominator = BitConverter.ToUInt32(propItem.Value, 12);
    float minutes = minutesNumerator / (float)minutesDenominator;

    uint secondsNumerator = BitConverter.ToUInt32(propItem.Value, 16);
    uint secondsDenominator = BitConverter.ToUInt32(propItem.Value, 20);
    float seconds = secondsNumerator / (float)secondsDenominator;

    float coorditate = degrees + (minutes / 60f) + (seconds / 3600f);
    string gpsRef = System.Text.Encoding.ASCII.GetString(new byte[1]  { propItemRef.Value[0] } ); //N, S, E, or W
    if (gpsRef == "S" || gpsRef == "W")
        coorditate = 0 - coorditate;
    return coorditate;
}
11
Paul

Вот код работает, я нашел несколько ошибок, работающих с float ... Я надеюсь, что это поможет кому-то.

  private static double ExifGpsToDouble (PropertyItem propItemRef, PropertyItem propItem)
    {
        double degreesNumerator = BitConverter.ToUInt32(propItem.Value, 0);
        double degreesDenominator = BitConverter.ToUInt32(propItem.Value, 4);
        double degrees = degreesNumerator / (double)degreesDenominator;

        double minutesNumerator = BitConverter.ToUInt32(propItem.Value, 8);
        double minutesDenominator = BitConverter.ToUInt32(propItem.Value, 12);
        double minutes = minutesNumerator / (double)minutesDenominator;

        double secondsNumerator = BitConverter.ToUInt32(propItem.Value, 16);
        double secondsDenominator = BitConverter.ToUInt32(propItem.Value, 20);
        double seconds = secondsNumerator / (double)secondsDenominator;


        double coorditate = degrees + (minutes / 60d) + (seconds / 3600d);
        string gpsRef = System.Text.Encoding.ASCII.GetString(new byte[1] { propItemRef.Value[0] }); //N, S, E, or W
        if (gpsRef == "S" || gpsRef == "W")
            coorditate = coorditate*-1;     
        return coorditate;
    }
11
Cesar

Я знаю, что это старый пост, но я хотел дать ответ, который помог мне. 

Я использовал ExifLibrary, расположенный здесь для записи и чтения метаданных из файлов изображений: https://code.google.com/p/exiflibrary/wiki/ExifLibrary

Это было просто и легко в использовании. Я надеюсь, что это помогает кому-то еще. 

4
Jason Foglia

Сначала вы должны прочитать байты, которые указывают, имеют ли данные EXIF ​​формат с прямым или прямым порядком байтов, чтобы вы не испортили все.

Затем вам нужно отсканировать каждое IFD изображения в поисках тега GPSInfo (0x25 0x88), если вы НЕ найдете этот тег внутри какого-либо IFD, это означает, что на изображении нет информации GPS. Если вы найдете этот тег, прочитайте 4 байта его значений, что дает вам смещение к другому IFD, GPS IFD, внутри этого IFD вам нужно только извлечь значения следующих тегов:

0x00 0x02 - для широты

0x00 0x04 - для долготы

0x00 0x06 - для высоты

Каждое из этих значений является беззнаковым рациональным.

Здесь вы можете найти, как сделать почти все: http://www.media.mit.edu/pia/Research/deepview/exif.html

3
Delta

Вы пробовали теги 0x0013-16 на http://msdn.Microsoft.com/en-us/library/ms534416(v=vs.85).aspx которые также выглядят как широта GPS? 

Не уверен, что отличает их от тегов с меньшим номером, но стоит попробовать.

Вот их описания:

0x0013 - Строка символов с нулевым символом в конце, указывающая, является ли широта точки назначения северной или южной широтой. N обозначает северную широту, а S обозначает южную широту.

0x0014 - Широта пункта назначения. Широта выражается в виде трех рациональных значений, дающих соответственно градусы, минуты и секунды. Когда выражены градусы, минуты и секунды, формат будет дд/1, мм/1, сс/1. Когда используются градусы и минуты и, например, доли минут задаются с точностью до двух десятичных знаков, форматом является дд/1, мммм/100, 0/1.

0x0015 - Строка символов с нулевым символом в конце, указывающая, является ли долгота пункта назначения восточной или западной долготой. E указывает восточную долготу, а W - западную долготу.

0x0016 - Долгота пункта назначения. Долгота выражается в виде трех рациональных значений, дающих соответственно градусы, минуты и секунды. Когда выражены градусы, минуты и секунды, формат будет ddd/1, mm/1, ss/1. Когда используются градусы и минуты и, например, доли минут задаются с точностью до двух десятичных знаков, формат будет ddd/1, mmmm/100, 0/1.

2
tomfanning

Спасибо, код в порядке, но по крайней мере один неудачник,

uint minutes = minutesNumerator/minutesDenominator;

дать не точный результат, когда minutesDenominator не равен 1, например, в моем exif = 16,

1
Claus