使用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.getAbsolutePath())).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);
}