我的一个项目会在读取图片流时实时对图像进行下采样。这种下采样可以将图像的尺寸缩小到所需的宽度和高度,而不需要进行昂贵的重置计算或修改磁盘上的图像。
由于我将图像下采样到较小的尺寸,因此还显著降低了显示所需的处理能力和内存。为了进一步优化,我也会将缓冲图像以瓦片的方式渲染...但这已经超出了本讨论的范围。您可以尝试以下方法:
public static BufferedImage subsampleImage(
ImageInputStream inputStream,
int x,
int y,
IIOReadProgressListener progressListener) throws IOException {
BufferedImage resampledImage = null;
Iterator<ImageReader> readers = ImageIO.getImageReaders(inputStream);
if(!readers.hasNext()) {
throw new IOException("No reader available for supplied image stream.");
}
ImageReader reader = readers.next();
ImageReadParam imageReaderParams = reader.getDefaultReadParam();
reader.setInput(inputStream);
Dimension d1 = new Dimension(reader.getWidth(0), reader.getHeight(0));
Dimension d2 = new Dimension(x, y);
int subsampling = (int)scaleSubsamplingMaintainAspectRatio(d1, d2);
imageReaderParams.setSourceSubsampling(subsampling, subsampling, 0, 0);
reader.addIIOReadProgressListener(progressListener);
resampledImage = reader.read(0, imageReaderParams);
reader.removeAllIIOReadProgressListeners();
return resampledImage;
}
public static long scaleSubsamplingMaintainAspectRatio(Dimension d1, Dimension d2) {
long subsampling = 1;
if(d1.getWidth() > d2.getWidth()) {
subsampling = Math.round(d1.getWidth() / d2.getWidth());
} else if(d1.getHeight() > d2.getHeight()) {
subsampling = Math.round(d1.getHeight() / d2.getHeight());
}
return subsampling;
}
要从文件中获取ImageInputStream,请使用:
ImageIO.createImageInputStream(new File("C:\\image.jpeg"));
可以看到,这个实现方式也尊重图像的原始宽高比。您可以选择注册一个IIOReadProgressListener,以便跟踪已经读取了图像的多少。如果正在通过网络读取图像,则这非常有用,可以用来显示进度条...但不是必需品,您也可以指定为null。
为什么这对您的情况特别重要?它从不将整个图像读入内存,只读取所需要的部分以使其以所需的分辨率显示。对于巨大的图像,甚至那些在磁盘上达到数十MB的图像,它都能够很好地工作。