使用 libexif 和 libjpeg 在现有 JPEG 上设置 exif 标签

Posted

技术标签:

【中文标题】使用 libexif 和 libjpeg 在现有 JPEG 上设置 exif 标签【英文标题】:Use libexif with libjpeg to set exif tags on an existing JPEG 【发布时间】:2018-01-03 12:25:09 【问题描述】:

在我的 C 程序中,我想使用 libexiflibjpeg 在给定路径 inputFilePath 上存在的现有 jpeg 文件上设置 exif 标签strong>,并将生成的 jpeg 保存到输出路径 outputFilePath

输入的 jpeg 文件很大(40000 X 40000 像素),因此将整个图像加载到内存中并不可取,也不需要。

我不关心 Jpeg 中其他现有的 Exif 标签,它们可能会被删除。

我已经阅读并尝试了 libexif 提供的使用固定 JPEG 的示例,但不知道如何对任何 JPEG 执行相同操作。

顺便说一句,我确实得到了以下代码,它通过加载 jpeg in-memory 来设置 exif 标记以工作。它使用与 libexif 一起提供的 exif 实用程序中提供的 libjpeg 实现。

ExifEntry *entry;
ExifData *exif = exif_data_new();
if (!exif) 
  //Out of memory


/* Set the image options */
exif_data_set_option(exif, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION);
exif_data_set_data_type(exif, EXIF_DATA_TYPE_COMPRESSED);
exif_data_set_byte_order(exif, FILE_BYTE_ORDER);

/* Create the mandatory EXIF fields with default data */
exif_data_fix(exif);

/* All these tags are created with default values by exif_data_fix() */
/* Change the data to the correct values for this image. */
entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_PIXEL_X_DIMENSION);
exif_set_long(entry->data, FILE_BYTE_ORDER, w);

entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_PIXEL_Y_DIMENSION);
exif_set_long(entry->data, FILE_BYTE_ORDER, h);

entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_COLOR_SPACE);
exif_set_short(entry->data, FILE_BYTE_ORDER, 1);

/* Create a EXIF_TAG_USER_COMMENT tag. This one must be handled
 * differently because that tag isn't automatically created and
 * allocated by exif_data_fix(), nor can it be created using
 * exif_entry_initialize() so it must be explicitly allocated here.
 */
entry = create_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_USER_COMMENT,
                   sizeof(ASCII_COMMENT) + sizeof(FILE_COMMENT) - 2);
/* Write the special header needed for a comment tag */
memcpy(entry->data, ASCII_COMMENT, sizeof(ASCII_COMMENT) - 1);
/* Write the actual comment text, without the trailing NUL character */
memcpy(entry->data + 8, FILE_COMMENT, sizeof(FILE_COMMENT) - 1);
/* create_tag() happens to set the format and components correctly for
 * EXIF_TAG_USER_COMMENT, so there is nothing more to do. */

JPEGData *jdata;
unsigned char *d = NULL;
unsigned int ds;
ExifLog *log = NULL;

/* Parse the JPEG file. */
jdata = jpeg_data_new();
jpeg_data_log(jdata, log);
jpeg_data_load_file(jdata, inputFilePath);

/* Make sure the EXIF data is not too big. */
exif_data_save_data(exif, &d, &ds);
if (ds) 
  free(d);
  if (ds > 0xffff)
    //Too much EXIF data
;

jpeg_data_set_exif_data(jdata, exif);

/* Save the modified image. */
jpeg_data_save_file(jdata, outputFilePath);
jpeg_data_unref(jdata);

【问题讨论】:

libjpeg(在 exif 0.6.22 中)在存在其他 APP1 标记(例如 XMP)时会损坏 jpeg 文件,因为它会盲目地将每个 APP1 标记解释为 exif 数据并写出损坏的 exif 部分。跨度> 【参考方案1】:

如果您不重新压缩或编辑图像,则不需要libjpeg。可以通过fopenfputc 完成。

exiv2 对 JPEG 文件结构和元数据进行了很好的描述。大多数 jpeg 文件将以 0xFFD8(图像开头)开头,然后是用于 JFIF 数据的 APP0 块(0xFF E0 <length> <data>)。如果有 EXIF 标头,则它位于 APP1 块中 (0xFF E1 <length> <data>)。

JPEG 文件中的块格式为

标记 (0xFF xx),其中 xxEn 代表 APPn

内容

2 个字节 - 以字节为单位的内容长度包括这 2 个字节 数据

所以,你的程序大纲应该是

    将文件复制到APP1 块 改写APP1 块 复制文件的其余部分

可以在libexif中使用exif_data_save_data()创建EXIF头内容。

【讨论】:

是否存在 exif 标签的值可以位于 app1 标记之外的情况?例如在文件标记结束之后。在这种情况下,算法将不起作用 根据 exif 规范和我看到的来源,所有 exif 标签都在 app1 块中。当然,您不能保证您阅读的任何文件都正确遵循规范。并且不一定有 app1 块,因此如果您要为生产代码执行此操作,则需要添加故障保护。

以上是关于使用 libexif 和 libjpeg 在现有 JPEG 上设置 exif 标签的主要内容,如果未能解决你的问题,请参考以下文章

Libexif 未定义的引用错误

Libexif ,附加新的 exif 数据

使用 Visual Studio 2010 将 libexif 编译为静态库 - 然后从 Visual C++ 项目链接

libjpeg-turbo的压缩速度和我程序中的libjpeg没有区别

Android压缩图片和libjpeg库

解决使用 libjpeg 保存图片时因磁盘写入失败导致程序退出的问题