Получение данных GPS из EXIF ​​изображения в C # - PullRequest
26 голосов
/ 13 февраля 2011

Я разрабатываю систему, которая позволяет загружать изображение на сервер с использованием 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.

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

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

Ответы [ 8 ]

24 голосов
/ 14 февраля 2011

Согласно ссылке , размещенной выше 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.

16 голосов
/ 25 сентября 2015

Простой (и быстрый!) Способ - использовать мой открытый исходный код 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 .

11 голосов
/ 18 июня 2013

Вот код, работающий, я нашел несколько ошибок, работающих с 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 голосов
/ 18 января 2013

Я наткнулся на это в поисках способа получить данные 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;
}
4 голосов
/ 08 января 2015

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

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

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

3 голосов
/ 14 февраля 2011

Сначала вы должны прочитать байты, которые указывают, имеют ли данные 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

2 голосов
/ 13 февраля 2011

Вы пробовали теги 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.

1 голос
/ 24 февраля 2013

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

uint minutes = minutesNumerator / minutesDenominator;

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

...