我在.NET中有一个Image.如何通过使用EXIF数据对其生成的纬度和经度进行编码来对图像进行地理标记,但无需使用外部库?
可以使用PropertyItems将EXIF信息附加到.NET 2.0及更高版本中的映像,PropertyItems可转换为单个EXIF字段.这些字段的详细信息可以在EXIF 2.3标准中找到,但我们只需要其中五个来对图像进行地理标记.下面的示例C#代码需要引用System.Drawing,System.Drawing.Imaging和System.IO.要测试它,只需使用下面的行.您可以通过使用此工具(或许多其他工具之一)检查图像来验证图像是否已正确地进行了地理标记.
Geotag(new Bitmap(@"C:\path\to\image.jpg"), 34, -118) .Save(@"C:\path\to\geotagged.jpg", ImageFormat.Jpeg);
下面的代码可能看起来很奇怪的一件事是,PropertyItem被重用来创建一个新的PropertyItem.由于改变现有的PropertyItem(它是一个类而不是一个struct)似乎会影响现有的属性,因此这种方法并不明显.然而,事实证明并非如此,并且这个hack是必要的,因为PropertyItem没有公共构造函数.
static Image Geotag(Image original, double lat, double lng) { // These constants come from the CIPA DC-008 standard for EXIF 2.3 const short ExifTypeByte = 1; const short ExifTypeAscii = 2; const short ExifTypeRational = 5; const int ExifTagGPSVersionID = 0x0000; const int ExifTagGPSLatitudeRef = 0x0001; const int ExifTagGPSLatitude = 0x0002; const int ExifTagGPSLongitudeRef = 0x0003; const int ExifTagGPSLongitude = 0x0004; char latHemisphere = 'N'; if (lat < 0) { latHemisphere = 'S'; lat = -lat; } char lngHemisphere = 'E'; if (lng < 0) { lngHemisphere = 'W'; lng = -lng; } MemoryStream ms = new MemoryStream(); original.Save(ms, ImageFormat.Jpeg); ms.Seek(0, SeekOrigin.Begin); Image img = Image.FromStream(ms); AddProperty(img, ExifTagGPSVersionID, ExifTypeByte, new byte[] { 2, 3, 0, 0 }); AddProperty(img, ExifTagGPSLatitudeRef, ExifTypeAscii, new byte[] { (byte)latHemisphere, 0 }); AddProperty(img, ExifTagGPSLatitude, ExifTypeRational, ConvertToRationalTriplet(lat)); AddProperty(img, ExifTagGPSLongitudeRef, ExifTypeAscii, new byte[] { (byte)lngHemisphere, 0 }); AddProperty(img, ExifTagGPSLongitude, ExifTypeRational, ConvertToRationalTriplet(lng)); return img; } static byte[] ConvertToRationalTriplet(double value) { int degrees = (int)Math.Floor(value); value = (value - degrees) * 60; int minutes = (int)Math.Floor(value); value = (value - minutes) * 60 * 100; int seconds = (int)Math.Round(value); byte[] bytes = new byte[3 * 2 * 4]; // Degrees, minutes, and seconds, each with a numerator and a denominator, each composed of 4 bytes int i = 0; Array.Copy(BitConverter.GetBytes(degrees), 0, bytes, i, 4); i += 4; Array.Copy(BitConverter.GetBytes(1), 0, bytes, i, 4); i += 4; Array.Copy(BitConverter.GetBytes(minutes), 0, bytes, i, 4); i += 4; Array.Copy(BitConverter.GetBytes(1), 0, bytes, i, 4); i += 4; Array.Copy(BitConverter.GetBytes(seconds), 0, bytes, i, 4); i += 4; Array.Copy(BitConverter.GetBytes(100), 0, bytes, i, 4); return bytes; } static void AddProperty(Image img, int id, short type, byte[] value) { PropertyItem pi = img.PropertyItems[0]; pi.Id = id; pi.Type = type; pi.Len = value.Length; pi.Value = value; img.SetPropertyItem(pi); }