如何在.NET中添加EXIF信息以进行图像地理标记?

4

我在.NET平台上有一张图片。如何使用EXIF数据对图片进行地理标记,即将它产生的纬度和经度编码到图片中,但是又不需要使用外部库?

1个回答

11

在.NET 2.0及以上版本中,可以使用PropertyItems将EXIF信息附加到图像中,这些信息对应于各个单独的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(它是类而不是结构体)似乎会影响现有的属性,因此这不是显而易见的。然而,事实证明这并非如此,这个技巧是必要的,因为 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);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接