java - 如何在Java中将.jpeg图像转换为.jif? (从EXIF到JFIF)

标签 java type-conversion jpeg

我有客户要求以.JIF格式(JFIF)发送图像。我有Java应用程序,但是我不会用google anith来讨论如何转换为该图像类型的主题,我什至很难用google anith来本身使用“ .JIF”格式。

编辑:
有人可以建议如何在Java中将Exif图像转换为JFIF吗?以及如何在此JFIF图像中添加注释?
(尝试使用jheader库可悲地以nullpointer异常结束,在Google上没有更多选择。)

最佳答案

编辑:将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信息将丢失。这样,将不允许您添加评论。

要添加注释,您仍然可以使用标准的ImageIO API,但是我们必须访问元数据,使代码更加冗长。有关元数据格式的更多信息,请参见JPEG Metadata Format Specification。如果您需要转换Exif元数据中的注释,请更新您的问题以使其更具体,因为它需要进一步解析元数据和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并添加了注释。

现在,还有另一种选择,可以做到完全无损。但这确实需要对JIF段结构以及Exif和JFIF格式如何工作有更深入的了解。不幸的是,没有标准的Java API(我知道)可以做到这一点,因此您必须自己动手。随意使用我的JPEG segment parsing code作为起点。您链接的JHeader项目看起来也非常有前途,但是我对该库没有任何经验,因此在此我无法提供任何建议。

这是基本思想:


解析/跳过标记段,直到SOS(扫描开始)段(SOS之后的数据将是压缩图像数据)。
编写SOI标记(0xffd8
创建一个APP0 /“ JFIF”标记(我想您可以在这里使用默认值,有关详细信息,请参见JFIF segment)。您可以为拇指尺寸写0、0,并跳过写缩略图数据。
添加带有所需注释的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标记开始,紧随其后的是带有“ JFIF”(以空终止)为标识符的APP0标记。根据original JFIF specification“ JPEG文件交换格式与标准JPEG交换格式完全兼容;唯一的附加要求是在SOI标记之后必须强制存在APP0标记。” (本部分未包含在规范的ITU和ISO版本中,但仍然适用)。简而言之,JFIF将JPEG数据限制为1或3分量,编码为Y或YCbCr,并强烈建议使用基线DCT,霍夫曼编码压缩。

另一种常见格式是Exif。此格式以SOI标记开始,紧随其后的是带有“ Exif”(以空终止)为标识符的APP1标记。此格式由数码相机制造商开发,并允许将更丰富的元数据记录在文件中(以TIFF元数据结构的形式)。据我了解,Exif使用基线DCT,霍夫曼编码压缩将JPEG数据限制为3个分量(编码为YCbCr)(最后一部分可能只是互操作性建议,规范中的语言有点难以理解。 ..)。

这两种格式都包含相同的“段”布局,并且图像数据是兼容的,但是由于要求将“其”标记作为流中的第一段,因此它们仍然是互斥的(因此,“存在“第三”格式,该格式是JFIF,用于兼容,但仍包含用于更丰富元数据的Exif段。

另一个“ JPEG文件”家族既没有JFIF也没有Exif标记,但是仍然遵循相同的段布局,带有SOI,APPn标记,SOF,DHT,DQT,SOS和EOI标记,如“附录B”(JIF)中所述。大多数解码器也会对这些图像进行解码。

TL; DR:总而言之,所有“ JPEG”文件格式的共同点是,它们使用JPEG压缩并遵循JIF结构。因此,很难理解有人“将经典JPEG转换为JIF”的含义。

“经典JPEG”是JIF。

关于java - 如何在Java中将.jpeg图像转换为.jif? (从EXIF到JFIF),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29028564/

相关文章:

gif - 如何从一堆 jpg 中创建 gif 动画

C#:如何将 BITMAP 字节数组转换为 JPEG 格式?

java - 可以向 JBUTTON 添加 html img 吗?

java - 在 Java 中如何将 char 转换为 int?

在C中将字符串转换为十六进制整数

c# - 将可为空的数字转换为字符串

c - 图像恢复程序有问题

用于分析计算的 Java 设计模式

java - 无法将 android.app.Fragment 转换为 mapping.SupportMapFragment

java - 如果至少有一个有效收件人,则发送邮件