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



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

这就是我加载图像的方式:

File file = new File("/Users/the21st/slow2.jpg");
BufferedImage image = ImageIO.read(file);

有什么方法可以提高Java中渐进编码的大分辨率JPEG的读取性能吗?

有问题的图片是这个(主持人,请不要再次重新上传到其他托管网站,这样编码/质量就不会改变)

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

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

BufferedImage image = ImageIO.read(file); 

在我的电脑(MacBookPro/2.8GHz i7)上读取图像大约需要18秒

使用我的ImageIO JPEG插件,它使用稍微不同的代码路径(即,通过获得ImageReader并调用readRaster()方法,然后从中创建BufferedImage,你可能会得到相同的结果。代码很简单,所以如果你想看代码,请参阅项目页面):

BufferedImage image = ImageIO.read(file); 

在我的计算机上读取图像大约需要8秒

使用我的BufferedImageFactory类和AWTToolkit:

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的性能简直太荒谬了,我真的很想知道为什么。如前所述,它似乎必须进行渐进解码,但仍然应该比这更好。

更新:

为了好玩,我创建了一个由LibJPEG-TurboJavaneneneba API支持的PoImageReader插件。它还不是很复杂,但它允许这样的代码:

BufferedImage image = ImageIO.read(file); 

要读取<1.5秒

PS:我曾经维护JMagick的ImageIO包装器(类似于@Jordan Doyle提到的代码,但它允许您根据ImageIO API进行编程),但我停止了,因为这太多工作了。也许我必须重新考虑。。。如果您不介意依赖JNI/native代码安装,那么至少也值得查看一下他的解决方案。

ImageIO的一个更快的替代方案是ImageMagick,有各种包装器用于通过Java与ImageMagick接口,如JMagick

要从JMagick获得BufferedImage,您必须首先获得MagickImage的实例,可以这样做:

ImageInfo info = new ImageInfo(pathToImage);
MagickImage image = new MagickImage(info);

现在,您可以使用我们自己的Jacob Nordfalk 8年前提供的方法将图像读取到BufferedImage中

public static BufferedImage magickImageToBufferedImage(MagickImage magickImage) throws Exception
{
Dimension  dim = magickImage.getDimension();
int size = dim.width * dim.height;
byte[] pixels = new byte[size * 3];
magickImage.dispatchImage(0, 0, dim.width, dim.height, "RGB", pixels);
BufferedImage bimage = createInterleavedRGBImage(dim.width, dim.height, pixels);
ColorModel cm = bimage.getColorModel();
Raster raster = bimage.getData();
WritableRaster writableRaster = null;
writableRaster = (raster instanceof WritableRaster) ? (WritableRaster) raster : raster.createCompatibleWritableRaster();
BufferedImage bufferedImage = new BufferedImage(cm, writableRaster, false, null);
return bufferedImage;
}

然后采用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);
}

最新更新