以纯 Java 读取图像、修改元数据和重写图像
Read Image, modify metadata, and re-write image in pure Java
我需要能够更新图像元数据(即标签、创建者、描述、评论)
并在常规 Exif 和 XMP 中进行。最有可能的是,我将阅读 Exif,并编写 XMP。
经过大量搜索也可用于写作的库后,我遇到了 twelvemonkeys。
https://github.com/haraldk/TwelveMonkeys
这似乎很有希望。事实上,我不费吹灰之力就已经能够阅读其中一张图片中包含描述的 Exif。请注意,不是使用标准的 javax API,而是使用 twelvemonkeys API。这对我来说没问题。什么都行!
在这一点上,我很高兴尽可能避免使用标准 API,因为它看起来非常复杂且效率低下。我开始阅读我的 Exif,并为我的概念验证编写修改代码。这个想法是,实现我想要的(快速安全地修改 JPEG 文件中的元数据)的最有效方法是执行以下步骤:
- 将所有段读入列表
- 找到需要修改的段
- 进行修改
- 按顺序将所有段写入临时文件
- 如果一切顺利,重命名原始文件以便以后安全删除,将副本重命名为原始文件,最后删除原始文件。
然而,当我发现
似乎没有实现时,我有点沮丧
com.twelvemonkeys.imageio.metadata.Directory
实现方法
add(Entry)
和
remove(Object)
除了
throw new UnsupportedOperationException("Directory is read-only");
如果这不是有效(和安全)实现我想做的事情的方法...有没有人建议我如何在纯粹的 Java 中做到这一点?
免责声明:我设计和编写的各种元数据readers/writers主要用于内部在我的ImageIO
库中使用,并没有仔细考虑第三方使用派对。因此,API 在这个意义上可能不是 "perfect"。但是你想做的应该是完全可行的。 :-)
虽然特定的 Directory
实现确实是只读的,但您可以轻松地创建自己的 AbstractDirectory
可变子类。或者只是使用任何你喜欢的 Collection<? extends Entry>
并在写入之前将其包装在 TIFFDirectory
或 IFD
中。我更喜欢后者,所以我先展示一下。
请注意,典型的 JPEG Exif 片段包含两个 IFD,主 JPEG 图像的 IFD0 和缩略图的 IFD1。因此你需要把它当作 CompoundDirectory
:
CompoundDirectory exif = (CompoundDirectory) new TIFFReader().read(input);
List<Directory> ifds = new ArrayList<>;
for (int i = 0; i < exif.directoryCount(); i++) {
List<Entry> entries = new ArrayList<>();
for (Entry entry : exif.getDirectory(i)) {
entries.add(entry);
}
// TODO: Do stuff with entries, remove, add, change, etc...
ifds.add(new IFD(entries));
}
// Write Exif
new TIFFWriter().write(new TIFFDirectory(ifds), output);
您也可以创建自己的可变 Directory
:
public final class MutableDirectory extends AbstractDirectory {
public MutableDirectory (final Collection<? extends Entry> entries) {
super(entries);
}
public boolean isReadOnly() {
return false;
}
// NOTE: While the above is all you need to make it *mutable*,
// TIFF/Exif does not allow entries with duplicate IDs,
// you need to handle this somehow. The below code is untested...
@Override
public boolean add(Entry entry) {
Entry existing = getEntryById(entry.getIdentifier());
if (existing != null) {
remove(existing);
}
super.add(entry);
}
}
不实现可变目录的原因恰恰是条目处理方式的语义可能因格式而异。
我需要能够更新图像元数据(即标签、创建者、描述、评论) 并在常规 Exif 和 XMP 中进行。最有可能的是,我将阅读 Exif,并编写 XMP。
经过大量搜索也可用于写作的库后,我遇到了 twelvemonkeys。
https://github.com/haraldk/TwelveMonkeys
这似乎很有希望。事实上,我不费吹灰之力就已经能够阅读其中一张图片中包含描述的 Exif。请注意,不是使用标准的 javax API,而是使用 twelvemonkeys API。这对我来说没问题。什么都行!
在这一点上,我很高兴尽可能避免使用标准 API,因为它看起来非常复杂且效率低下。我开始阅读我的 Exif,并为我的概念验证编写修改代码。这个想法是,实现我想要的(快速安全地修改 JPEG 文件中的元数据)的最有效方法是执行以下步骤:
- 将所有段读入列表
- 找到需要修改的段
- 进行修改
- 按顺序将所有段写入临时文件
- 如果一切顺利,重命名原始文件以便以后安全删除,将副本重命名为原始文件,最后删除原始文件。
然而,当我发现
似乎没有实现时,我有点沮丧com.twelvemonkeys.imageio.metadata.Directory
实现方法
add(Entry)
和
remove(Object)
除了
throw new UnsupportedOperationException("Directory is read-only");
如果这不是有效(和安全)实现我想做的事情的方法...有没有人建议我如何在纯粹的 Java 中做到这一点?
免责声明:我设计和编写的各种元数据readers/writers主要用于内部在我的ImageIO
库中使用,并没有仔细考虑第三方使用派对。因此,API 在这个意义上可能不是 "perfect"。但是你想做的应该是完全可行的。 :-)
虽然特定的 Directory
实现确实是只读的,但您可以轻松地创建自己的 AbstractDirectory
可变子类。或者只是使用任何你喜欢的 Collection<? extends Entry>
并在写入之前将其包装在 TIFFDirectory
或 IFD
中。我更喜欢后者,所以我先展示一下。
请注意,典型的 JPEG Exif 片段包含两个 IFD,主 JPEG 图像的 IFD0 和缩略图的 IFD1。因此你需要把它当作 CompoundDirectory
:
CompoundDirectory exif = (CompoundDirectory) new TIFFReader().read(input);
List<Directory> ifds = new ArrayList<>;
for (int i = 0; i < exif.directoryCount(); i++) {
List<Entry> entries = new ArrayList<>();
for (Entry entry : exif.getDirectory(i)) {
entries.add(entry);
}
// TODO: Do stuff with entries, remove, add, change, etc...
ifds.add(new IFD(entries));
}
// Write Exif
new TIFFWriter().write(new TIFFDirectory(ifds), output);
您也可以创建自己的可变 Directory
:
public final class MutableDirectory extends AbstractDirectory {
public MutableDirectory (final Collection<? extends Entry> entries) {
super(entries);
}
public boolean isReadOnly() {
return false;
}
// NOTE: While the above is all you need to make it *mutable*,
// TIFF/Exif does not allow entries with duplicate IDs,
// you need to handle this somehow. The below code is untested...
@Override
public boolean add(Entry entry) {
Entry existing = getEntryById(entry.getIdentifier());
if (existing != null) {
remove(existing);
}
super.add(entry);
}
}
不实现可变目录的原因恰恰是条目处理方式的语义可能因格式而异。