编辑:将 Exif JPEG 转换为 JFIF JPEG:
如果您不介意损失一些质量(由于有损 JPEG 重新编码),您可以将图像转换为:
File inFile = ...;
File outFile = ...; // Feel free to use ".jif" as extension
if (!ImageIO.write(ImageIO.read(inFile), "JPEG", outFile)) {
System.err.println("Could not write JPEG format"); // Should never happen
}
这会起作用,因为默认的JPEGImageWriter 插件只支持JFIF 格式。而且因为我们不读取元数据,旧的 Exif 信息只会丢失。但是,这样做将不允许您添加 cmets。
要添加 cmets,您仍然可以使用标准 ImageIO API,但我们必须访问元数据,从而使代码更加冗长。有关元数据格式的更多信息,请参阅JPEG Metadata Format Specification。如果您需要从 Exif 元数据转换 cmets,请将您的问题更新为具体的,因为它需要进一步解析元数据和 ImageIO API 目前没有的额外支持。
File inFile = ...;
File outFile = ...; // Feel free to use ".jif" as extension
BufferedImage image = ImageIO.read(inFile);
ImageWriter jpegWriter = ImageIO.getImageWritersByFormatName("JPEG").next(); // Should be a least one
// To write comments, we need to add it to the metadata
ImageWriteParam param = jpegWriter.getDefaultWriteParam();
IIOMetadata metadata = jpegWriter.getDefaultImageMetadata(ImageTypeSpecifier.createFromRenderedImage(image), param);
IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree("javax_imageio_jpeg_image_1.0");
IIOMetadataNode markerSequence = (IIOMetadataNode) root.getElementsByTagName("markerSequence").item(0); // Should be only one
// Insert a "COM" marker, with our comment
IIOMetadataNode com = new IIOMetadataNode("com");
com.setAttribute("comment", "Hello JFIF!");
markerSequence.appendChild(com);
// Merge edited metadata
metadata.mergeTree("javax_imageio_jpeg_image_1.0", root);
ImageOutputStream output = ImageIO.createImageOutputStream(outFile);
try {
jpegWriter.setOutput(output);
// Write image along with metadata
jpegWriter.write(new IIOImage(image, null, metadata));
}
finally {
output.close();
}
jpegWriter.dispose();
这样,我们仍然将图像重新编码为有损 JPEG,但我们将 Exif 转换为 JFIF 并添加 cmets。
现在,还有另一种选择,可以完全无损地执行此操作。但这确实需要对 JIF 段结构以及 Exif 和 JFIF 格式的工作原理有更深入的了解。不幸的是,没有标准的Java API(据我所知)可以做到这一点,所以你必须自己动手。随意使用我的JPEG segment parsing code 作为起点。您链接的 JHeader 项目看起来也很有前途,但我对这个库没有任何经验,所以我不能在那里提供任何建议。
这是基本的想法:
- 解析/跳过标记段,直到 SOS(扫描开始)段(SOS 之后的数据将是压缩的图像数据)。
- 写入 SOI 标记 (
0xffd8)
- 创建一个 APP0/"JFIF" 标记(我认为您可以在这里使用默认值,详情请参阅JFIF segment)。您可以为缩略图尺寸写入 0、0,并跳过写入缩略图数据。
- 使用您需要的任何 cmets 添加您的 COM 段(可能从 Exif 元数据中提取)
- 按原样从原始流中编写 SOF、DHT、DQT 等标准片段(跳过 APP1/“Exif”和其他“自定义”片段)。
- 从原始流中写入 SOS 标记和图像数据
理论上,这应该可行。您可能会遇到一些较小的色彩空间问题,因为 Exif 数据可能包含不同的色彩空间(通常是 sRGB 或 AdobeRGB1998),而 JFIF doesn't have a defined color space.如果您需要,请添加一个带有所需配置文件的 APP2/“ICC_PROFILE”段(在第 3 步之后)。
祝你好运! :-)
注意:这不是一个完整的答案,而是试图澄清为什么您需要与您的客户交谈,并弄清楚您的 JPEG 有什么问题以及他所说的“JIF”的实际含义。
首先,JPEG 不是一种文件格式。 JPEG 是静止图像压缩标准。该标准的一部分(通常称为“附件 B”)是对交换格式的描述,有时称为 JIF。该标准还指定了一种称为 SPIFF 的完整文件格式,但这种格式并不是很普遍(而且我认为这不是您想要的)。
随处可见的文件,称为“JPEG 文件”(我假设这就是您所说的“经典 JPEG”),通常是基本相同文件格式的两种略有不同的风格之一:
最基本的格式是JFIF。此格式以 SOI 标记开始,紧随其后的是 APP0 标记,其标识符为“JFIF”(空终止)。根据original JFIF specification“JPEG 文件交换格式与标准 JPEG 交换格式完全兼容;唯一的附加要求是在 SOI 标记之后必须存在 APP0 标记。” (这部分被排除在规范的ITU 和 ISO 版本之外,但仍然适用)。简而言之,JFIF 将 JPEG 数据限制为 1 或 3 个分量,编码为 Y 或 YCbCr,并强烈推荐基线 DCT,霍夫曼编码压缩。
另一种常见格式是Exif。此格式以 SOI 标记开始,紧随其后的是 APP1 标记,其标识符为“Exif”(空终止)。这种格式由数码相机制造商开发,允许在文件中记录更丰富的元数据(以 TIFF 元数据结构的形式)。据我了解,Exif 将 JPEG 数据限制为 3 个分量,编码为 YCbCr,使用基线 DCT、霍夫曼编码压缩(最后一部分可能只是互操作性建议,规范中的语言有点难以阅读。 ..)。
这两种格式都包含相同的“段”布局并且图像数据是兼容的,但它们仍然是互斥的,因为要求将“他们的”标记作为流中的第一个段(因此,还存在“第三种”格式,它是 JFIF 以实现兼容性,但仍包含 Exif 段以提供更丰富的元数据。
另一类“JPEG 文件”缺少 JFIF 和 Exif 标记,但仍遵循相同的段布局,包括 SOI、APPn 标记、SOF、DHT、DQT、SOS 和 EOI 标记,如“附件 B”中所述(JIF)。大多数解码器也会解码这些图像。
TL;DR:总而言之,所有“JPEG”文件格式的共同点是它们使用 JPEG 压缩,并遵循 JIF 结构。正因为如此,有点难以理解某人所说的“将经典 JPEG 转换为 JIF”是什么意思。
“经典 JPEG”是 JIF。