如何从流中读取图像?

 NAVETEX 发布于 2022-12-12 17:04

有人可能认为这BufferedImage是用Java处理图像的最佳选择.虽然很方便,但在阅读巨大的图像时,它往往最终会出现:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

增加VM大小不是解决方案,因为在我的情况下,某些输入文件非常庞大.

所以我正在寻找如何从流中逐步读取图像的方式.

我怀疑ImageIO.createImageInputStream()ImageIO可能适合该法案,但我不知道如何使用它逐步读取块.此外,JDK上有类PNGMetadataPNGImageReader可用的类rt.jar似乎很有用,但我没有找到它们的用法的简单示例.

这是要走的路,还是有更好的选择?

1 个回答
  • 用于读取和操作图像的Java API并不像您想象的那样基于流.的ImageInputStream仅仅是一个方便的包装,允许读入byte来自不同输入(S和其他原始类型RandomAccessFileS,InputStream小号等).

    我一直在考虑创建一个用于读取"像素流"的API,以允许在不使用大量内存的情况下链接处理过滤器.但是,从来没有认真对待这种努力.如果您想听到更多想法或有可行的实施,请随意雇用我.;-)

    不过,正如我所看到的,您可以通过多种方式实现最终目标,从而能够处理大型图像:

      BufferedImage按原样使用,以及ImageIOAPI以较小的部分读取图像以节省内存.由于实现的原因,这对某些格式非常有效,对其他格式效率较低(即默认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和基于磁盘的RenderedImages.再次,我将留下这个选项供您进一步探索.

    2022-12-12 17:05 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有