You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何使用LibTIFF为TIFF/DNG图像添加GPS元数据

嘿,我来帮你把LibTIFF添加GPS元数据的逻辑理清楚,解决你的两个困惑,再给你一份能用的C语言实现方案:

用LibTIFF为TIFF/DNG添加GPS元数据的C实现方案

先解决你的两个核心困惑

1. TIFFTAG_GPSIFD 和 TIFFTAG_SUBIFD 到底该用哪个?

咱们直接说结论:添加GPS元数据必须用TIFFTAG_GPSIFD,别碰TIFFTAG_SUBIFD

  • TIFFTAG_SUBIFD是通用的子IFD指针,主要用来放缩略图、多分辨率图像这类内容,是个“万能筐”。
  • TIFFTAG_GPSIFD是TIFF/DNG规范里专门给GPS信息留的标签,用来明确指向存储GPS数据的独立IFD目录。用它的话,其他软件(比如Lightroom、ExifTool)才能正确识别到GPS元数据,不会当成普通子IFD忽略掉。

2. 怎么正确构建GPS IFD?

你之前的思路方向对,但细节错了。正确的步骤是:

  1. 主IFD里先设置TIFFTAG_GPSIFD,初始值填0就行,LibTIFF会自动帮你修正成实际的GPS IFD偏移地址。
  2. 调用TIFFWriteDirectory()写完主IFD,这时候LibTIFF会自动切换到新的空白IFD,咱们就用这个作为GPS目录。
  3. 在这个GPS IFD里,用TIFFSetField()逐个设置GPS相关标签(注意每个标签的数据类型,比如GPSVersionID是字节数组,经纬度是有理数数组)。
  4. 所有GPS标签设置完后,再调用一次TIFFWriteDirectory(),把GPS IFD保存下来,这时候LibTIFF会自动更新主IFD里TIFFTAG_GPSIFD的正确偏移。

你之前的问题在于:用了SUBIFD而非GPSIFD,而且设置GPSVersionID时TIFFSetField()的参数格式完全错了——这个标签需要传入4字节的数组,不是零散的单个字节参数。

完整的C语言实现代码

下面是符合规范的实现,你可以参考这个逻辑集成到你的LuaJIT模块中:

#include <tiffio.h>
#include <stdio.h>
#include <math.h>

int add_gps_metadata_to_tiff(const char* filename, void* image_data, 
                             uint32_t width, uint32_t height, uint16_t sample_depth,
                             const double latitude, const double longitude, 
                             const uint8_t gps_version[4]) {
    TIFF* tif = TIFFOpen(filename, "w");
    if (!tif) {
        fprintf(stderr, "Failed to open TIFF file\n");
        return -1;
    }

    // --------------------------
    // 1. 设置主IFD的基础图像字段
    // --------------------------
    uint16_t bits_per_sample = sample_depth;
    uint16_t samples_per_pixel = 1; // 根据你的实际图像调整,比如RGB是3
    uint16_t photometric = PHOTOMETRIC_MINISBLACK; // 按需选择 photometric 类型
    uint32_t bytes_per_row = width * samples_per_pixel * (bits_per_sample / 8);

    TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width);
    TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height);
    TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bits_per_sample);
    TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, samples_per_pixel);
    TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric);
    TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); // 按需调整压缩方式
    TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
    TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, height); // 单条带存储图像
    TIFFSetField(tif, TIFFTAG_STRIPBYTECOUNTS, bytes_per_row);

    // 写入图像原始数据
    TIFFWriteEncodedStrip(tif, 0, image_data, bytes_per_row);

    // --------------------------
    // 2. 设置GPS元数据
    // --------------------------
    if (gps_version != NULL) {
        // 在主IFD中标记GPS IFD的位置,初始偏移设为0,LibTIFF会自动更新
        TIFFSetField(tif, TIFFTAG_GPSIFD, (uint32_t)0);

        // 写完主IFD,切换到新的空白IFD作为GPS目录
        if (!TIFFWriteDirectory(tif)) {
            fprintf(stderr, "Failed to write main IFD\n");
            TIFFClose(tif);
            return -1;
        }

        // --------------------------
        // 设置GPS IFD中的具体标签
        // --------------------------
        // GPSVersionID: 4字节数组,示例为GPS 2.2版本
        TIFFSetField(tif, TIFFTAG_GPSVERSIONID, 4, gps_version);

        // 示例:将十进制经纬度转换为度分秒的有理数格式
        // 纬度处理(北纬39.9042°为例)
        uint32_t lat_deg = (uint32_t)floor(latitude);
        double lat_frac = (latitude - lat_deg) * 60;
        uint32_t lat_min = (uint32_t)floor(lat_frac);
        double lat_sec = (lat_frac - lat_min) * 60;
        TIFFSetField(tif, TIFFTAG_GPSLATITUDE, 
                     (TIFFRational){lat_deg, 1}, 
                     (TIFFRational){lat_min, 1}, 
                     (TIFFRational){(uint32_t)(lat_sec * 10000), 10000});
        TIFFSetField(tif, TIFFTAG_GPSLATITUDEREF, "N"); // N=北纬,S=南纬

        // 经度处理(东经116.4074°为例)
        uint32_t lon_deg = (uint32_t)floor(longitude);
        double lon_frac = (longitude - lon_deg) * 60;
        uint32_t lon_min = (uint32_t)floor(lon_frac);
        double lon_sec = (lon_frac - lon_min) * 60;
        TIFFSetField(tif, TIFFTAG_GPSLONGITUDE, 
                     (TIFFRational){lon_deg, 1}, 
                     (TIFFRational){lon_min, 1}, 
                     (TIFFRational){(uint32_t)(lon_sec * 10000), 10000});
        TIFFSetField(tif, TIFFTAG_GPSLONGITUDEREF, "E"); // E=东经,W=西经

        // 可按需添加更多GPS标签,比如海拔、时间戳
        // TIFFSetField(tif, TIFFTAG_GPSALTITUDE, (TIFFRational){(uint32_t)(altitude*100), 100});
        // TIFFSetField(tif, TIFFTAG_GPSALTITUDEREF, 0); // 0=海平面以上,1=以下

        // 保存GPS IFD,LibTIFF会自动更新主IFD的GPSIFD偏移
        if (!TIFFWriteDirectory(tif)) {
            fprintf(stderr, "Failed to write GPS IFD\n");
            TIFFClose(tif);
            return -1;
        }
    }

    // 关闭文件,确保所有目录都写入磁盘
    TIFFClose(tif);
    return 0;
}

针对你现有代码的修正提示

  • 立刻把TIFFTAG_SUBIFD替换成TIFFTAG_GPSIFD,别用通用子IFD存储GPS信息。
  • 设置GPSVersionID时,要传入完整的4字节数组(比如uint8_t gps_ver[] = {2,2,0,0};),调用方式是TIFFSetField(tif, TIFFTAG_GPSVERSIONID, 4, gps_ver);,不是零散的单个字节。
  • 写完主IFD切换到GPS IFD后,设置完所有GPS标签必须再调用一次TIFFWriteDirectory(),否则GPS IFD不会被保存。

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

火山引擎 最新活动