有人可能认为这BufferedImage
是用Java处理图像的最佳选择.虽然很方便,但在阅读巨大的图像时,它往往最终会出现:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
增加VM大小不是解决方案,因为在我的情况下,某些输入文件非常庞大.
所以我正在寻找如何从流中逐步读取图像的方式.
我怀疑ImageIO.createImageInputStream()
从ImageIO
可能适合该法案,但我不知道如何使用它逐步读取块.此外,JDK上有类PNGMetadata
和PNGImageReader
可用的类rt.jar
似乎很有用,但我没有找到它们的用法的简单示例.
这是要走的路,还是有更好的选择?
用于读取和操作图像的Java API并不像您想象的那样基于流.的ImageInputStream
仅仅是一个方便的包装,允许读入byte
来自不同输入(S和其他原始类型RandomAccessFile
S,InputStream
小号等).
我一直在考虑创建一个用于读取"像素流"的API,以允许在不使用大量内存的情况下链接处理过滤器.但是,从来没有认真对待这种努力.如果您想听到更多想法或有可行的实施,请随意雇用我.;-)
不过,正如我所看到的,您可以通过多种方式实现最终目标,从而能够处理大型图像:
BufferedImage
按原样使用,以及ImageIO
API以较小的部分读取图像以节省内存.由于实现的原因,这对某些格式非常有效,对其他格式效率较低(即默认JPEGImageReader
将在将较小的区域移交给Java堆之前读取本机内存中的整个映像,但PNGImageReader
可能没问题).
有点像:
ImageInputStream stream = ImageIO.createImageInputStream(input); ImageReader reader = ImageIO.getImageReaders(stream).next(); // TODO: Test hasNext() reader.setInput(stream); int width = reader.getWidth(0); int height = reader.getHeight(0); ImageReadParam param = reader.getDefaultReadParam(); for (int y = 0; y < height; y += 100) { for (int x = 0; x < width; x += 100) { param.setSourceRegion(new Rectangle(x, y, 100, 100)); // TODO: Bounds check // Read a 100 x 100 tile from the image BufferedImage region = reader.read(0, param); // ...process region as needed... } }
立即读取整个图像,进入内存映射缓冲区.随意尝试一些我为此目的而做的实验课(使用nio
).读取将比读取纯存储器图像慢,并且处理速度也会变慢.但是,如果您一次对图像的较小区域进行计算,则可能与内存中的某些优化一样快.我已经使用这些类将> 1 GB图像读入32 MB JVM(实际内存消耗当然要大得多).
再次,这是一个例子:
ImageInputStream stream = ImageIO.createImageInputStream(input); ImageReader reader = ImageIO.getImageReaders(stream).next(); // TODO: Test hasNext() reader.setInput(stream); int width = reader.getWidth(0); int height = reader.getHeight(0); ImageTypeSpecifier spec = reader.getImageTypes(0).next(); // TODO: Test hasNext(); BufferedImage image = MappedImageFactory.createCompatibleMappedImage(width, height, spec) ImageReadParam param = reader.getDefaultReadParam(); param.setDestination(image); image = reader.read(0, param); // Will return same image as created above // ...process image as needed...
某些格式(如未压缩的TIFF,BMP,PPM等)会保留文件中的像素,使其可以直接对其进行内存映射以对其进行操作.需要一些工作,但应该是可能的.TIFF还支持可能有用的瓷砖.我将此选项作为练习,随意使用我上面链接的课程作为起点或灵感.;-)
JAI可能有一些可以帮助你的东西.我不是一个忠实的粉丝,因为甲骨文有许多未解决的错误和缺乏爱心和发展.但值得一试.我认为他们也支持tileable和基于磁盘的RenderedImage
s.再次,我将留下这个选项供您进一步探索.