【问题标题】:How to convert .jpeg image to .jif in java? (Exif TO JFIF)如何在java中将.jpeg图像转换为.jif? (Exif 转 JFIF)
【发布时间】:2015-03-26 17:06:56
【问题描述】:

客户要求我以 .JIF 格式 (JFIF) 发送图像。我有 java 应用程序,但我不能用谷歌搜索到如何转换为该图像类型的主题,我什至几乎不能用谷歌搜索到“.JIF”格式本身。

编辑: 有人可以建议如何在 java 中将 Exif 图像转换为 JFIF 吗?以及如何为这个 JFIF 图像添加评论? (尝试使用 jheader 库不幸以 nullpointer 异常结束,在 google 上没有更多选择。)

【问题讨论】:

  • 是 JIF 还是 JFIF(JPEG 文件交换格式)我找不到有关 JIF 的任何信息,如果是这种情况,那么您需要更新您的问题。
  • “经典 JPEG”(一个子集)JIF + 额外的 APPn 标记。通常,文件为 JFIF 或 Exif 格式。你确定你真的需要做任何转换吗?
  • 客户有特殊要求拒绝了我们的 JPEG 并要求 JIF。但我想知道如何从中获取 JIF(在 java 中)。谷歌对这个话题很简短。
  • @JiroMatchonson 那是因为没有人真正使用 JIF(有关详细信息,请参阅维基百科关于 JPEG 的文章)。你确定你不想要JFIF吗?你能展示一个你的客户拒绝的样本吗?你能问他“JIF”到底是什么意思吗?
  • @JiroMatchonson 或者你的意思是Jeff's Image Format? :-)

标签: java type-conversion jpeg


【解决方案1】:

编辑:将 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 项目看起来也很有前途,但我对这个库没有任何经验,所以我不能在那里提供任何建议。

这是基本的想法:

  1. 解析/跳过标记段,直到 SOS(扫描开始)段(SOS 之后的数据将是压缩的图像数据)。
  2. 写入 SOI 标记 (0xffd8)
  3. 创建一个 APP0/"JFIF" 标记(我认为您可以在这里使用默认值,详情请参阅JFIF segment)。您可以为缩略图尺寸写入 0、0,并跳过写入缩略图数据。
  4. 使用您需要的任何 cmets 添加您的 COM 段(可能从 Exif 元数据中提取)
  5. 按原样从原始流中编写 SOF、DHT、DQT 等标准片段(跳过 APP1/“Exif”和其他“自定义”片段)。
  6. 从原始流中写入 SOS 标记和图像数据

理论上,这应该可行。您可能会遇到一些较小的色彩空间问题,因为 Exif 数据可能包含不同的色彩空间(通常是 sRGB 或 Adob​​eRGB1998),而 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 specificationJPEG 文件交换格式与标准 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。

【讨论】:

  • 得到了客户的回应。你说的很可能是真的。我必须创建的图像是 *.jif 扩展名,但确实是 JFIF。从我们的设备中,我们得到了 Exif 图像。所以这个主题背后的真正问题是:“如何在 Java 中将 Exif 图像转换为 JFIF 图像”。 “以及如何向它们添加 cmets。”两个图像中的标题示例:Exif 图像:“ÿØÿá^B^@Exif^@^@II*^@^H^@^@^@...” jif/JFIF 需要:ÿØÿà^@^PJFIF^@^ A^A^A^@...” 所以在 java 中“Exif to JFIF”,我无法通过谷歌搜索合理的东西,只是 jheader.sourceforge.net 很遗憾不起作用(nullpointerException)。
  • Finnaly 得到了回复,似乎所有转换后的图像和 cmets 都很好,非常感谢! , 被接受为答案:)
【解决方案2】:

首先您需要读取该图像,然后您必须将该图像写入您想要的尺寸和格式。 您必须使用 ImageIO 类和 BufferedReader 来读取图像 要编写它们,请使用 Graphics2D 类 将格式名称替换为 jif

 File inputFile = new File(inputImagePath);
    BufferedImage inputImage = ImageIO.read(inputFile);

    // creates output image
    BufferedImage outputImage = new BufferedImage(scaledWidth,
            scaledHeight, inputImage.getType());

    // scales the input image to the output image
    Graphics2D g2d = outputImage.createGraphics();
    g2d.drawImage(inputImage, 0, 0, scaledWidth, scaledHeight, null);
    g2d.dispose();

    // extracts extension of output file
    String formatName = outputImagePath.substring(outputImagePath
            .lastIndexOf(".") + 1);

    // writes to output file
    ImageIO.write(outputImage, formatName, new File(outputImagePath));

【讨论】:

  • 您好,我到底是在哪里将 .JIF 格式设置为新图像?或者代码的哪一部分将此图像更改为 JIF ? BufferedImage 是否支持该类型?
  • 读取最后一行 ImageIO.write(outputImage ,"jif",new File(outputImagePath));
  • @JiroMatchonson 我已经测试了该代码,它在 png 、 jpeg 和其他标准图像格式上运行良好。但我认为 windows 无法识别这种格式,你需要额外的三分之一可以打开.jif的派对工具。不过你可以从link下载这个工具
  • 哦,听起来很酷,tnx 需要付出很多努力,主要是我需要它使图像处于正确有效的 .jif 类型,以便我们客户的设备可以识别它们(他们不会只识别 .jif 的经典 jpeg 图像那些)。 Tnx 非常努力,如果一切顺利,请尝试您的实现并标记为正确答案:)
  • 请注意,如果格式不受支持,`ImageIO.write(...)` 不会抛出异常,而是返回false。你应该检查这个返回值。我确信ImageIO NOT 支持任何名为“jif”的格式。
猜你喜欢
  • 2022-09-25
  • 2018-04-08
  • 2023-02-06
  • 1970-01-01
  • 1970-01-01
  • 2016-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多