如何在 Java 中压缩 jpeg 图像而不会丢失该图像中的任何元数据?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/948368/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 14:31:29  来源:igfitidea点击:

How can I compress jpeg images in Java without losing any metadata in that image?

javaimage-processingcompressionjpeg

提问by guitarpoet

I want compress jpeg files using Java. I do it like this:

我想使用 Java 压缩 jpeg 文件。我这样做:

  1. Read the image as BufferedImage
  2. Write the image to another file with compression rate.
  1. 读取图像为 BufferedImage
  2. 将图像以压缩率写入另一个文件。

OK, that seems easy, but I find the ICC color profile and the EXIF information are gone in the new file and the DPI of the image is dropped from 240 to 72. It looks different from the origin image. I use a tool like preview in OS X. It can perfectly change the quality of the image without affecting other information.

好吧,这看起来很简单,但我发现新文件中没有 ICC 颜色配置文件和 EXIF 信息,并且图像的 DPI 从 240 下降到 72。它看起来与原始图像不同。我用的是OS X中的preview之类的工具,可以完美的改变图片的质量,不影响其他信息。

Can I done this in Java? At least keep the ICC color profile and let the image color look the same as the origin photo?

我可以在 Java 中完成此操作吗?至少保持ICC颜色配置文件,让图像颜色看起来和原始照片一样?

采纳答案by Rezaul Karim

/**
 * @param inputFilenameWithPath : binary filepath
 * @param outputFilepath        : output image path
 * @param start                 : from where the image start in binary file
 * @param len                   : length of the image
 * @throws ImageAccessException
 */
public void extractImageFromBinaryFile(String inputFilenameWithPath, String outputFilepath, int start, int len) throws ImageAccessException
{
    try
    {
        File file = new File(inputFilenameWithPath);
        FileImageInputStream iis = new FileImageInputStream(file);

        // Added
        byte[] b = new byte[start];
        iis.read(b, 0, start);

        byte[] fb = new byte[]{};
        iis.read(fb);

        IIOByteBuffer iiob = new IIOByteBuffer(fb, start, len);
        iis.readBytes(iiob, len);

        OutputStream os = new FileOutputStream(outputFilepath);
        os.write(iiob.getData());
        iis.close();
        os.close();

    }
    catch (IOException ioe)
    {`enter code here`
        throw new ImageAccessException("Image File read/write error");
    }
}

回答by guitarpoet

Finally find a way to do this.

终于找到了一种方法来做到这一点。

Use javax.imageio.IIOImage. It can be readed by JpegImageReader.

使用 javax.imageio.IIOImage。它可以被 JpegImageReader 读取。

But there was a bug in it, a bug last for 6 years. :(

但是里面有一个bug,一个bug持续了6年。:(

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4924909

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4924909

Luckily, if you do some hack(Fake the JFIF section), and it works. :D

幸运的是,如果你做了一些 hack(伪造 JFIF 部分),它就可以工作。:D

So, this problem can be solved like this:

所以,这个问题可以这样解决:

  1. Use ImageIO to get the ImageReader for jpeg
  2. Read the jpeg image into a memory buffer
  3. If it fails at bug 4924909 then fix the image buffer with a fake JFIF information
  4. Use ImageWriter to write the file, let the ImageWriterParam to do the trick.
  1. 使用 ImageIO 获取 jpeg 的 ImageReader
  2. 将 jpeg 图像读入内存缓冲区
  3. 如果它在错误 4924909 处失败,则使用伪造的 JFIF 信息修复图像缓冲区
  4. 使用 ImageWriter 写入文件,让 ImageWriterParam 来完成。

Well, it seems to be ok(Every information saved), except for one thing, the output image is brighter(or rather say, pale) than the original image(It won't happen when I use preview of OS X to compressed photo, so the problem must be in my code or java, or my wrong usage :( ).

好吧,似乎没问题(保存了所有信息),除了一件事,输出图像比原始图像更亮(或者说,苍白)(当我使用 OS X 预览压缩照片时不会发生这种情况,所以问题一定出在我的代码或java,或者我的错误用法:()。

So, my problem for compressing jpeg in java is not solved yet.

所以,我在java中压缩jpeg的问题还没有解决。

Any suggestions?

有什么建议?