You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用C#为JPG图片写入经纬度元数据?(附问题代码)

修正JPG元数据写入经纬度的代码

你的代码存在几个核心问题:

  1. 复用同一个PropertyItem对象多次修改,易引发元数据写入异常
  2. 经纬度存储格式不符合EXIF规范:EXIF的GPS经纬度并非直接存储double值,而是以度分秒的有理数组(每个分量用分子/分母整数对表示)存储,且需配套方向标签(南北纬/东西经)

以下是修正后的完整代码:

using System.Drawing.Imaging;
using System.IO;
using System.Text;
using System.Reflection;

public static class BitmapExtensions
{
    public static Bitmap WriteCoordinatesToImage(this Bitmap sourceBitmap, double latitude, double longitude)
    {
        // 处理纬度:转换为度分秒格式,添加南北纬标签
        var latDms = ConvertDegreesToDms(latitude);
        AddGpsProperty(sourceBitmap, 0x0001, 2, Encoding.ASCII.GetBytes((latitude >= 0 ? "N" : "S") + '\0'));
        AddGpsProperty(sourceBitmap, 0x0002, 5, GetDmsBytes(latDms));

        // 处理经度:转换为度分秒格式,添加东西经标签
        var lonDms = ConvertDegreesToDms(longitude);
        AddGpsProperty(sourceBitmap, 0x0003, 2, Encoding.ASCII.GetBytes((longitude >= 0 ? "E" : "W") + '\0'));
        AddGpsProperty(sourceBitmap, 0x0004, 5, GetDmsBytes(lonDms));

        return sourceBitmap;
    }

    // 将小数度数转为度分秒
    private static (int Degrees, int Minutes, double Seconds) ConvertDegreesToDms(double degrees)
    {
        var absDeg = Math.Abs(degrees);
        int d = (int)absDeg;
        double rem = (absDeg - d) * 60;
        int m = (int)rem;
        double s = (rem - m) * 60;
        return (d, m, s);
    }

    // 将度分秒转为EXIF要求的有理数组字节
    private static byte[] GetDmsBytes((int Degrees, int Minutes, double Seconds) dms)
    {
        using var ms = new MemoryStream();
        using var bw = new BinaryWriter(ms);
        // 度:分子=度数,分母=1
        bw.Write(dms.Degrees);
        bw.Write(1);
        // 分:分子=分数,分母=1
        bw.Write(dms.Minutes);
        bw.Write(1);
        // 秒:用整数分子分母避免精度损失
        int secNum = (int)(dms.Seconds * 10000);
        bw.Write(secNum);
        bw.Write(10000);
        return ms.ToArray();
    }

    // 新增GPS属性项(解决PropertyItem无法直接实例化的问题)
    private static void AddGpsProperty(Bitmap bitmap, int propId, short propType, byte[] value)
    {
        PropertyItem prop;
        // 反射创建PropertyItem实例(因为无公开构造函数)
        var ctor = typeof(PropertyItem).GetConstructor(
            BindingFlags.Instance | BindingFlags.NonPublic,
            null, Type.EmptyTypes, null);
        prop = (PropertyItem)ctor.Invoke(null);

        prop.Id = propId;
        prop.Type = propType;
        prop.Len = value.Length;
        prop.Value = value;
        bitmap.SetPropertyItem(prop);
    }
}

重要说明:

  • EXIF GPS标签规范:
    • GPSLatitude(0x0002)和GPSLongitude(0x0004)的类型为5(有理类型),长度固定为24字节(3个度分秒分量×8字节/分量)
    • GPSLatitudeRef(0x0001)、GPSLongitudeRef(0x0003)为ASCII字符串类型(类型2),存储"N"/"S"或"E"/"W",必须以\0结尾
  • 由于PropertyItem无公开构造函数,通过反射创建新实例,避免复用现有属性导致的冲突

内容的提问来源于stack exchange,提问作者user19626293

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 00:54:10