在Java中读取渐进编码的9000x9000 JPEG需要1分钟

在Java中读取渐进编码的9000x9000 JPEG需要1分钟,java,jpeg,javax.imageio,Java,Jpeg,Javax.imageio,使用javax.imageio.imageio从磁盘加载大分辨率(9000x9000)JPEG时,在我的scala应用程序中需要1分钟以上的时间。我尝试创建一个只使用Java的项目,但它仍然需要太长的时间——大约30秒 这是我加载图像的方式: File file = new File("/Users/the21st/slow2.jpg"); BufferedImage image = ImageIO.read(file); 在Java中读取渐进编码的大分辨率JPEG时,是否有任何方法可以提高性

使用
javax.imageio.imageio
从磁盘加载大分辨率(9000x9000)JPEG时,在我的scala应用程序中需要1分钟以上的时间。我尝试创建一个只使用Java的项目,但它仍然需要太长的时间——大约30秒

这是我加载图像的方式:

File file = new File("/Users/the21st/slow2.jpg");
BufferedImage image = ImageIO.read(file);
在Java中读取渐进编码的大分辨率JPEG时,是否有任何方法可以提高性能


有问题的图片是(版主,请不要再上传到其他托管站点,这样编码/质量就不会改变)

一个比
ImageIO
更快的选择是
ImageMagick
,有各种包装器可以通过Java接口
ImageMagick
,例如

要从JMagick获取
buffereImage
,您必须首先获取
MagickImage
的实例,该实例可以如下操作:

ImageInfo info = new ImageInfo(pathToImage);
MagickImage image = new MagickImage(info);
现在,您可以使用我们8年前提供的方法将图像读入BuffereImage

然后使用
createInterleavedRGBImage
方法:

public static BufferedImage createInterleavedRGBImage(int imageWidth, int imageHeight, byte data[])
{
    int[] numBits = new int[3];
    int[] bandoffsets = new int[3];

    for (int i = 0; i < 3; i++) {
        numBits[i] = 8;
        bandoffsets[i] = i;
    }

    ComponentColorModel ccm = new ComponentColorModel(
        ColorSpace.getInstance(ColorSpace.CS_sRGB),
        numBits,
        false,
        false, //Alpha pre-multiplied
        Transparency.OPAQUE,
        DataBuffer.TYPE_BYTE
    );

    PixelInterleavedSampleModel csm = new PixelInterleavedSampleModel(
        DataBuffer.TYPE_BYTE,
        imageWidth,
        imageHeight,
        3, //Pixel stride
        imageWidth * 3, // Scanline stride
        bandoffsets
    );

    DataBuffer dataBuf = new DataBufferByte(data, imageWidth * imageHeight * 3);
    WritableRaster wr = Raster.createWritableRaster(csm, dataBuf, new Point(0, 0));
    return new BufferedImage(ccm, wr, false, null);
}
public static buffereImage createInterleavedRGBImage(int-imageWidth、int-imageHeight、字节数据[])
{
int[]numBits=新int[3];
int[]带偏移量=新的int[3];
对于(int i=0;i<3;i++){
numBits[i]=8;
带偏移量[i]=i;
}
ComponentColorModel ccm=新ComponentColorModel(
ColorSpace.getInstance(ColorSpace.CS_sRGB),
麻木的,
假,,
false,//Alpha预乘
透明,不透明,
DataBuffer.TYPE_字节
);
PixelInterleavedSampleModel csm=新的PixelInterleavedSampleModel(
DataBuffer.TYPE_字节,
图像宽度,
图像高度,
3,//像素步长
imageWidth*3,//扫描线跨距
带偏移
);
DataBuffer dataBuf=新的DataBufferByte(数据,图像宽度*图像高度*3);
WritableRaster wr=Raster.createWritableRaster(csm、dataBuf、新点(0,0));
返回新的BuffereImage(ccm、wr、false、null);
}

好的,这是我到目前为止的发现(老实说,它们有点令人担忧……)

使用与Oracle JRE捆绑的ImageIO标准JPEG插件:

BufferedImage image = ImageIO.read(file); 
在我的电脑(MacBookPro/2.8GHz i7)上大约用18秒读取图像

使用my,它使用稍微不同的代码路径(即,通过获取
ImageReader
并调用
readRaster()
方法,然后从中创建
BuffereImage
,您可能会得到相同的结果。代码非常重要,如果您想查看代码,请参阅项目页面):

在我的电脑上大约用8秒读取图像

使用我的类和AWT
工具包

BufferedImage image = new BufferedImageFactory(Toolkit.getDefaultToolkit().createImage(file.getAbsolutePat‌​h())).getBufferedImage();
在我的计算机上以2.5秒的时间读取图像

使用
sun.awt.codec
中不推荐使用的
JPEGImageDecoder
类:

BufferedImage image = new JPEGImageDecoderImpl(new FileInputStream(file)).decodeAsBufferedImage();
在我的计算机上以~1.7秒的时间读取图像

因此,这意味着我们应该能够在不到2秒内读取此图像,即使是在Java中。在这种情况下,
JPEGImageReader
的性能简直是荒谬可笑,我很想知道原因。如前所述,它似乎必须使用渐进解码,但仍然应该比这更好

更新:

为了好玩,我创建了一个由。它还不是很复杂,但它允许以下代码:

BufferedImage image = ImageIO.read(file); 
在我的电脑上以<1.5秒读取图像


PS:(类似于@Jordan Doyle提到的代码,但它允许您针对ImageIO API编程),但是我停止了,因为这是一项太多的工作。也许我得重新考虑一下。。。如果你不介意依赖JNI/本机代码安装,至少也值得看看他的解决方案。

@Bret我没有意识到StackOverflow可能在上传到他们的服务器时调整了图像的大小和/或重新编码了图像。这是原始图像:(使用此链接编辑原始问题)嗯,似乎渐进编码是罪魁祸首。如果我将您的样本重新编码为基线,则读取时间将从~40s->~2s缩短。可能您正在进行GC搅动,请尝试增加堆内存。我已投票决定重新打开。“是否有比ImageIO更快的方法可以加载JPEG图像?”不一定是推荐的工具。我有很多编程建议。由于使用了不同的代码路径,我的计算机读取图像的速度是原来的两倍(在我的计算机上,JRE:~18秒,TM:~8秒,ymmv)。这意味着,如果您获得JRE
ImageReader
for JPEG并使用
readRaster
方法,您将看到相同的速度提升。似乎是由于渐进编码,正如@barti_ddu PS所指出的那样:投票赞成重新打开,但还不能回答…有没有办法修改你的
BufferedImageFactory
方法,以便它也可以解码CMYK JPEG?@The21th抱歉,似乎
Toolkit.createImage
方法不支持CMYK,所以它无法工作(而且我们也没有什么办法来修复它)。不过,我的原始插件和LibJPEG-Turbo版本都有。;-)这篇文章的评论太晚了。我的电脑速度很慢,使用Windows 10/JDK14,并尝试加载图像:ImageIO.read(文件)需要50秒,但JavaFX new JavaFX.scene.image.image()只需要约3.5秒。@DuncG有趣。ImageIO插件至少应该有一些快捷方式来避免渐进式更新。但是,你看到的差别仍然很大。
BufferedImage image = new JPEGImageDecoderImpl(new FileInputStream(file)).decodeAsBufferedImage();
BufferedImage image = ImageIO.read(file);