从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中,给出不是确切的结果,