Подтвердить что ты не робот

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

Я разрабатываю систему, которая позволяет загружать изображение на сервер с помощью ASP.NET С#. Я обрабатываю изображение, и все работает отлично. Мне удалось найти метод, который считывает данные Дата создания EXIF ​​и анализирует его как 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.

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

Спасибо заранее.

4b9b3361

Ответ 1

В соответствии с ссылкой, опубликованной выше по 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);

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

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

Ответ 2

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

Ответ 3

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

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

var location = gps.GetGeoLocation();

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

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

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

Ответ 4

Вот код работает, я нашел некоторые ошибки, работающие с 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;
    }

Ответ 5

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

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

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

Ответ 6

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

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

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

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

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

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

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

Ответ 7

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

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

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

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

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

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

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

Ответ 8

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

uint minutes = minutesNumerator/minutesDenominator;

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