从C#中的图像EXIF获取GPS数据

我正在开发一个系统,允许使用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 \ 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() .FirstOrDefault(); var location = gps.GetGeoLocation(); Console.WriteLine("Image at {0},{1}", location.Latitude, location.Longitude); 

该库是用纯C#编写的,支持许多图像格式,并解码特定于许多相机型号的数据。

它可以通过NuGet或GitHub获得 。

我试图找到一种方法将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; } 

我知道这是一个老post,但我想提供一个帮助我的答案。

我使用位于此处的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

你有没有试过标签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。

谢谢,代码很好,但至少有一个失败者,

uint minutes = minutesNumerator / minutesDenominator;

当minutesDenominator不等于1,例如在我的exif = 16中,给出不是确切的结果,