我将图像以ImageIcons的形式存储在数据库中,我希望将其提供给我们的网页,但是对于大图像,我会遇到内存异常。
以下是我目前的工作方式,
[编辑] 我扩展了我的ImageUtilities,提供了一个非透明的BufferedImage,简化了代码,
BufferedImage rgbbi = ImageUtilities.toBufferedImage(icon.getImage());
ServletOutputStream out = null;
try {
// Get the Servlets output stream.
out = responseSupplier.get().getOutputStream();
// write image to our piped stream
ImageIO.write(rgbbi, "jpg", out);
} catch (IOException e1) {
logger.severe("Exception writing image: " + e1.getMessage());
} finally {
try {
out.close();
} catch (IOException e) {
logger.info("Error closing output stream, " + e.getMessage());
}
}
抛出的异常如下,
Exception in thread "Image Fetcher 0" java.lang.OutOfMemoryError: Java heap space
at java.awt.image.DataBufferInt.<init>(DataBufferInt.java:41)
at java.awt.image.Raster.createPackedRaster(Raster.java:458)
at java.awt.image.DirectColorModel.createCompatibleWritableRaster(DirectColorModel.java:1015)
at sun.awt.image.ImageRepresentation.createBufferedImage(ImageRepresentation.java:230)
at sun.awt.image.ImageRepresentation.setPixels(ImageRepresentation.java:484)
at sun.awt.image.ImageDecoder.setPixels(ImageDecoder.java:120)
at sun.awt.image.JPEGImageDecoder.sendPixels(JPEGImageDecoder.java:97)
at sun.awt.image.JPEGImageDecoder.readImage(Native Method)
at sun.awt.image.JPEGImageDecoder.produceImage(JPEGImageDecoder.java:119)
at sun.awt.image.InputStreamImageSource.doFetch(InputStreamImageSource.java:246)
at sun.awt.image.ImageFetcher.fetchloop(ImageFetcher.java:172)
at sun.awt.image.ImageFetcher.run(ImageFetcher.java:136)
Exception in thread "Image Fetcher 0" java.lang.OutOfMemoryError: Java heap space
Exception in thread "Image Fetcher 0" java.lang.OutOfMemoryError: Java heap space
Exception in thread "Image Fetcher 0" java.lang.OutOfMemoryError: Java heap space
...
有没有办法可以重写这个来流式传输ImageIO.write
的输出并以某种方式限制其缓冲区大小?
[编辑]
我不仅可以增加堆大小,我需要服务的图像在10000x7000像素范围内,作为一个字节数组,可以得到(10000px x 7000px x 24bits)
280MB。我认为这是一个不合理的堆大小,可以分配给servlet中的图像转换。
示例图片Large
答案 0 :(得分:1)
我假设您的屏幕上没有足够的像素来显示完整的图像。由于您似乎需要在RAM中为显示器提供未压缩的版本,因此您需要的堆量与图像大小所暗示的完全相同。话虽如此,还有很多更好的方法。
我写了我的学士论文,有效地同时显示高达40000x40000像素的多个大图像。我们最终实现了具有多级缓存的LOD。意思是图像被调整大小,每个大小被切成方块,产生image pyramid。为了找到最佳的块大小,我们不得不进行一些实验。它因系统而异,但可以安全地假设在64x64到256x256像素之间。
接下来的事情是实现用于上传正确的chuncks的调度算法,以保持texel:pixel的1:1的比率。为了获得更好的质量,我们在金字塔的切片之间使用了三线性插值。
“多级”意味着图像块被上传到图形卡的VRAM,其中RAM作为L1缓存而HD作为L2缓存(如果图像在网络上),但这种优化可能过多。你的情况。
总而言之,这是很多要考虑的事情,而你只是要求内存控制。如果这是一个重大项目,那么实施LOD是该工作的正确工具。
答案 1 :(得分:1)
正如评论中所指出的,将10000x7000图像存储在数据库中,如ImageIcons,并通过servlet提供它们,闻起来就像糟糕的设计。 不过,我指出了这个PNGJ库(免责声明:我编写了它),它允许您逐行读取/写入PNG中的图像。当然,只有在以这种格式存储大图像时,这才有用。
答案 2 :(得分:0)
你无法使用你正在使用的内置类来实现这一点,因为它们的目的是为了批量生产位图。你可能最好通过Image Magick(或者现在的任何东西)来运行这些java。
你只需要做一次吗?
您可能不得不自己编写所有内容,加载文件,处理“像素”并将其写出来。这将是最好的方法,而不是加载整个东西,转换(即复制)它,并写出来。我不知道Image Magick之类的东西是否适用于流或内存图像。
AlexR的补遗:
要做到这一点,他需要将文件解码为某种可流式格式。例如,JPEG将图像分成8x8块,单独压缩,然后将这些块流出。当它将块流出时,块本身被压缩(因此,如果你有10个黑色块,你会得到一个黑色块,数量为10)。
原始位图只是字节块,对于带有alpha的高色空间,它是4个字节(红色,绿色,蓝色和Alpha各一个)。大多数色彩空间转换都发生在像素级别。其他更复杂的滤镜适用于像素和周围像素(高斯模糊是一个简单的例子)。
为简单起见,特别是对于许多不同的格式,更容易“将整个图像加载到内存中”,处理其原始位图,在转换时复制该位图,然后将原始图像写回任何内容格式(例如,将颜色JPEG转换为灰度PNG)。
对于大型图像,就像这个人正在处理的那样,它的内存非常昂贵。
所以,最好的是,他会编写特定的代码来分批读取文件,将其流入,转换每一点,然后再将其流回。这只需要很少的记忆,但他可能不得不自己完成大部分工作。
所以,是的,他可以“只是逐字节地读取图像”,但处理和算法可能会相当复杂。
答案 3 :(得分:0)
更多的记忆似乎是转换的唯一答案,而我不必自己编写。
我的解决方案就是不转换图像并使用this answer中描述的方法来检索图像mime类型以便能够设置标题。