使用 BufferedImage 和 ImageIO 将其转换为 byte[] 后图像大小变小 [英] Image size getting decreased after converting it into byte[] using BufferedImage and ImageIO

查看:67
本文介绍了使用 BufferedImage 和 ImageIO 将其转换为 byte[] 后图像大小变小的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用以下代码将图像转换为字节 [].

public static byte[] extractBytes (String ImageName) 抛出 IOException {ByteArrayOutputStream baos=new ByteArrayOutputStream();BufferedImage img=ImageIO.read(new File(ImageName));ImageIO.write(img, "jpg", baos);返回 baos.toByteArray();}

现在当我测试我的代码时:

public static void main(String[] args) 抛出 IOException {String filepath = "image_old.jpg";文件输出=新文件(文件路径);System.out.println("原始图片大小="+outp.length());字节 [] 数据 = 提取字节(文件路径);System.out.println("字节大小[] data="+data.length);BufferedImage img = ImageIO.read(new ByteArrayInputStream(data));//再次将byte[]数组转换为图像File outputfile = new File("image_new.jpg");ImageIO.write(img, "jpeg", outputfile);System.out.println("转换图像的大小="+outputfile.length());}

我得到了非常奇怪的结果:

原图大小=78620字节[]数据的大小=20280转换图像的大小=20244

将图像转换为 byte[] 后,它的大小减少了大约 1/4,而且当我将 byte[] 转换回图像时,它的大小会改变.但输出图像已成功在所需位置创建.放大 500-600% 后,我可以看到原始图像和新图像的质量略有不同.放大后新图像有点模糊.

这是我正在进行测试的图像 http://pbrd.co/1BrOVbf

请解释这种尺寸变化的原因,并且我想知道在此之后获得相同尺寸的任何方法.

解决方案

您拥有的图像以最高质量设置(ImageIO 术语中的100%"或 1.0)压缩.JPEG 压缩在如此高的设置下不是很有效,因此比平常大很多.使用 ImageIO.write(..., JPEG", ...) 时,将使用默认质量设置.此默认值为 0.75(尽管此值的确切含义取决于编码器,但并非精确科学),因此质量较低,导致文件大小较小.

(原始图像和重新压缩图像之间文件大小如此显着减小的另一个可能原因是元数据的删除.使用 ImageIO.read(file) 读取时,您是有效地去除 JPEG 文件中的任何元数据,如 XMP、Exif 或 ICC 配置文件.在极端情况下(是的,我在这里主要谈论 Photoshop ;-))此元数据可能比图像数据本身占用更多空间(即,兆字节的元数据是可能的).但是,对于您的文件,情况并非如此.)

正如您从第二次重新压缩(从 byte[] 到最终输出文件)中看到的,输出仅略小于输入.这是因为质量设置(未指定,因此仍使用默认值)在两种情况下都相同(此外,在此步骤中任何元数据也将丢失,因此不会增加文件大小).细微差别可能是由于 JPEG 解压缩/重新压缩过程中的一些小损失(舍入误差等)所致.

虽然有点违反直觉,但在重新压缩 JPEG 时,最少的数据丢失(就原始图像的变化而言,而不是文件大小),总是通过使用相同质量设置(使用完全相同的表应该实际上是无损的,但可能仍会出现小的舍入误差)作为原始.提高质量设置会使文件输出更大,但实际上质量会下降.

100% 确保不丢失任何数据或图像质量的唯一方法是首先不对图像进行解码/编码,而是逐字节复制文件,例如像这样:

文件在 = ...;文件输出 = ...;InputStream input = new FileInputStream(in);尝试 {OutputStream output = new FileOutputStream(out);尝试 {复制(输入,输出);}最后 {输出关闭();}}最后 {input.close();}

和复制方法:

public void copy(final InputStream in, final OutputStream out) {字节[]缓冲区=新字节[1024];整数计数;while ((count = in.read(buffer)) != -1) {out.write(buffer, 0, count);}//刷新流,写入任何剩余的缓冲数据out.flush();}

I am converting an Image into byte[] using following code.

public static byte[] extractBytes (String ImageName) throws IOException {

   ByteArrayOutputStream baos=new ByteArrayOutputStream();
        BufferedImage img=ImageIO.read(new File(ImageName));
        ImageIO.write(img, "jpg", baos);
        return baos.toByteArray();
    }

Now when I am testing my code:

public static void main(String[] args) throws IOException {
    String filepath = "image_old.jpg";
    File outp=new File(filepath);
    System.out.println("Size of original image="+outp.length());
    byte[] data = extractBytes(filepath);
    System.out.println("size of byte[] data="+data.length);
    BufferedImage img = ImageIO.read(new ByteArrayInputStream(data));
    //converting the byte[] array into image again
    File outputfile = new File("image_new.jpg");
    ImageIO.write(img, "jpeg", outputfile);
    System.out.println("size of converted image="+outputfile.length());
}

I am getting very strange results:

Size of original image=78620 
size of byte[] data=20280
size of converted image=20244

After converting image into byte[], its size getting decreased by around 1/4th and also when I am converting byte[] back to image its size alters.But output image is successfully getting created in the desired location. I can see the slight difference in quality of the original image and new image after doing 500-600 % zoom in. New image is little blurred after zoom in.

Here is the image on which I am doing the testing http://pbrd.co/1BrOVbf

Please explain the reason of this change in size and also I want to know any method to get the same size after this.

解决方案

The image you have is compressed with maximum quality setting ("100%" or 1.0 in ImageIO terms). JPEG compression isn't very effective at such high settings, and is thus quite a bit larger than usual. When using ImageIO.write(..., "JPEG", ...) the default quality setting will be used. This default is 0.75 (the exact meaning of such a value is encoder dependent though, and isn't exact science), and thus lower quality, resulting in a smaller file size.

(Another likely cause for such a significant decrease in file size between the original and the re-compressed image, is the removal of meta data. When reading using ImageIO.read(file) you are effectively stripping away any meta data in the JPEG file, like XMP, Exif or ICC profiles. In extreme cases (yes, I'm talking mainly about Photoshop here ;-)) this meta data can take up more space than the image data itself (ie. megabytes of meta data is possible). This is however, not the case for your file.)

As you can see from the second re-compression (from byte[] to final output file), the output is just slightly smaller than the input. This is because the quality setting (unspecified, so still using default) will be the same in both cases (also, any metadata would also be lost in this step, so not adding to the file size). The minor difference is likely due to some small losses (rounding errors etc) in the JPEG decompression/re-compression.

While slightly counter-intuitive, the least data-loss (in terms of change from the original image, not in file size) when re-compression a JPEG, is always achieved by re-compression with the same quality setting (using the exact same tables should be virtually lossless, but small rounding errors might still occur) as the original. Increasing the quality setting will make the file output larger, but the quality will actually degrade.

The only way to be 100% sure to not lose any data or image quality, is by not decoding/encoding the image in the first place, but rather just copy the file byte by byte, for instance like this:

File in = ...;
File out = ...;

InputStream input = new FileInputStream(in);
try {
    OutputStream output = new FileOutputStream(out);
    try {
        copy(input, output);
    }
    finally {
        output.close();
    }
}
finally {
    input.close();
}

And the copy method:

public void copy(final InputStream in, final OutputStream out) {
    byte[] buffer = new byte[1024]; 
    int count;

    while ((count = in.read(buffer)) != -1) {
        out.write(buffer, 0, count);
    }

    // Flush out stream, to write any remaining buffered data
    out.flush();
}

这篇关于使用 BufferedImage 和 ImageIO 将其转换为 byte[] 后图像大小变小的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆