簡體   English   中英

在 C# 中從圖像的 EXIF 中獲取 GPS 數據

[英]Getting GPS data from an image's EXIF in C#

我正在開發一個允許使用 ASP.NET C# 將圖像上傳到服務器的系統。 我正在處理圖像,一切正常。 我設法找到了一種讀取日期創建 EXIF 數據並將其解析為 DateTime 的方法。 這也很好用。

我現在正在嘗試從 EXIF 讀取 GPS 數據。 我想捕捉緯度和經度數字。

我使用這個列表作為對 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 數據。

我知道那里有一些課程可以做到這一點,但我更願意自己編寫 - 不過我願意接受建議:-)

提前致謝。

根據 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。

一個簡單(而且快速!)的方法是使用我的開源MetadataExtractor庫:

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

var location = gps.GetGeoLocation();

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

該庫是用純 C# 編寫的,支持多種圖像格式並解碼特定於多種相機型號的數據。

它可以通過NuGetGitHub 獲得

我遇到了這個尋找一種方法來獲取 EXIF GPS 數據作為一組浮點數。 我已經修改了 Jon Grant 的代碼如下......

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

這是代碼工作,我發現了一些使用浮動的錯誤。 我希望這對某人有幫助。

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

我知道這是一篇舊帖子,但我想提供一個對我有幫助的答案。

我使用位於此處的 ExifLibrary 從圖像文件中寫入和讀取元數據: https : //code.google.com/p/exiflibrary/wiki/ExifLibrary

這很簡單且易於使用。 我希望這對其他人有幫助。

您首先必須讀取指示 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

您是否嘗試過每個http://msdn.microsoft.com/en-us/library/ms534416(v=vs.85).aspx也看起來像 GPS 緯度的標簽 0x0013-16?

不確定這些與編號較低的標簽有何區別,但值得一試。

以下是他們的說明:

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。

謝謝,代碼很好,但至少有一個失敗者,

uint 分鍾 = minutesNumerator / minutesDenominator;

給出不准確的結果,當分鍾分母不等於 1 時,例如在我的 exif=16 中,

如果您使用ImageMagick庫,這非常簡單。

假設您從圖像流開始:

  1. 轉換為 MagickImage 對象
  2. 獲取exif信息
  3. 使用上面的@Cesar 答案獲取坐標

像這樣:

     //1
using (MagickImage image = new MagickImage(photoBlob))
                    {                  
                        var exifData = image.GetExifProfile();
                        
                        if (exifData != null)
                        {
                            //2  
                            var gpsLong = exifData.GetValue(ExifTag.GPSLongitude);
                            var gpsLongRef = exifData.GetValue(ExifTag.GPSLongitudeRef);
                            var gpsLatitude = exifData.GetValue(ExifTag.GPSLatitude);
                            var gpsLatitudeRef = exifData.GetValue(ExifTag.GPSLatitudeRef);
    
                            var longitude = GetCoordinates(gpsLongRef.ToString(), gpsLong.Value);
                            var latitude = GetCoordinates(gpsLatitudeRef.ToString(), gpsLatitude.Value);
                        }
                    }
        
//3     
      private static double GetCoordinates(string gpsRef, Rational[] rationals)
            {
                double degrees = rationals[0].Numerator / rationals[0].Denominator;
                double minutes = rationals[1].Numerator / rationals[1].Denominator;
                double seconds = rationals[2].Numerator / rationals[2].Denominator;
    
                double coordinate = degrees + (minutes / 60d) + (seconds / 3600d);
                if (gpsRef == "S" || gpsRef == "W")
                    coordinate *= -1;                 
               return coordinate;
            }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM