有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

用java压缩/压缩图像文件

One of the image file used

    String dirName=System.getProperty("user.home") + "/deleteme";
    ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream(1000);
    BufferedImage bufferedImage=ImageIO.read(new File(dirName,"a.jpg"));
    ImageIO.write(bufferedImage, "jpg", byteArrayOutputStream);
    byteArrayOutputStream.flush();

    byteArrayOutputStream.close();

    byte[] bytearray = byteArrayOutputStream.toByteArray();

    BufferedImage imag=ImageIO.read(new ByteArrayInputStream(bytearray));
    ImageIO.write(imag, "jpg", new File(dirName,"snap.jpg"));

这段代码读取图像文件,将其转换为字节数组,然后将其存储回去。 现在,对于我测试的大多数图像文件,大小都急剧减小到原始大小的15%左右

  1. 有人能解释为什么这样做会减少图像文件的大小吗

  2. 在这个过程中质量会下降吗

[我比较了http://driiqm.mpi-inf.mpg.de中的图像,结果显示新图像具有相同的质量。]


共 (3) 个答案

  1. # 1 楼答案

    质量正在更改为默认值。使用此默认值写入两次

    ImageIO.write(bufferedImage, "jpg", byteArrayOutputStream);
    

    现在,图像已转换为字节,并使用有损压缩进行压缩

    ImageIO.write(imag, "jpg", new File(dirName,"snap.jpg"));
    

    它还使用默认压缩

    try (ByteArrayOutputStream out = new ByteArrayOutputStream(1000)) {
            float quality = 1f;
            ImageWriter writer = ImageIO.getImageWritersBySuffix("jpg").next();
    
            ImageWriteParam param = writer.getDefaultWriteParam();
            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
            param.setCompressionQuality(1f);
            IIOImage image = new IIOImage(img, new ArrayList<>(), null);
            writer.setOutput(ImageIO.createImageOutputStream(out));
            writer.write(null, image, param);
            byte[] bytearray = out.toByteArray();
            System.out.println(bytearray.length);
            Files.write(Paths.get("snap2.jpg"), bytearray, StandardOpenOption.WRITE);
    
        }
    

    在本例中,字节数组将包含最小的有损压缩,接近输入大小。要写出来,不要使用ImageIO再次读/写。相反,我只是将字节直接写入文件

  2. # 2 楼答案

    你可以这样做:

    try (OutputStream out = new FileOutputStream(new File(dirName,"snap.jpg"))) {
        JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
        JPEGEncodeParam eparm = encoder.getDefaultJPEGEncodeParam(img);
        eparm.setQuality(quality, true);
        encoder.setJPEGEncodeParam(eparm);
        encoder.encode(img);
    }
    

    其中质量是介于0之间的浮动。和1

  3. # 3 楼答案

    TLDR:您的结果与原始结果的质量不同。JPEG总是会降低一些质量,但您的结果会降低很多质量。Exif元数据已经丢失,但差别不大

    我用你的写作方法测试了你的示例图像,然后用最高质量测试了它(例如@MauricePerry编写的代码)。然后我将它们与http://driiqm.mpi-inf.mpg.dehttp://exif.regex.info/exif.cgi进行比较

    所以原始文件:

    用你的方法我得到了:

    • 大小为35kB
    • 没有嵌入颜色配置文件
    • SamplingYCbCr4:2:0(2)
    • 原始和结果之间有很多差异:

    comparison with first result

    我的产品质量上乘:

    • 大小为272kB
    • 没有嵌入颜色配置文件
    • SamplingYCbCr4:2:0(2)
    • 原始和结果之间的最小差异(但仍有一些):

    comparison with second result

    我不确定你是怎么认为图片的质量是一样的,因为你发布的网页显示了很多差异。即使是最高质量的,它也会显示出差异,这是有意义的,因为JPEG是有损格式BufferedImage将图像数据表示为位图,转换为JPEG时会丢失一些信息。这就是为什么我们有像PNG这样的无损格式

    我也照你说的做了——使用结果图像作为源图像,然后输入和输出是一样的。我会把它比作达到当地最低标准。JPEG->;位图转换生成位图,其中位图->;JPEG生成相同的JPEG数据,因为您使用相同的功能(相同的质量、相同的采样)。但如果位图->;JPEG转换使用了不同的功能(例如这次使用最高质量),质量损失会更多