【问题标题】:Appending to an Image File附加到图像文件
【发布时间】:2011-05-20 17:25:01
【问题描述】:

我编写了一个程序,它可以拍摄一张“照片”,并为它选择的每个像素插入一系列其他照片中的图像。选择的图像是平均颜色最接近照片原始像素的照片。

我首先对“库存”图像中每个像素的 rgb 值进行平均,然后将其转换为 CIE LAB,这样我就可以根据人类感知来计算它与相关像素的“接近”程度颜色。

然后我编译了一个图像,其中原始“照片”图像中的每个像素都被替换为“最近的”库存图像。

效果很好,效果很好,但是库存图像大小为 300 x 300 像素,甚至使用“-Xms2048m -Xmx2048m”的虚拟机标志,是的,我知道这是可笑的,在 555 像素 x 540 像素的图像上我可以仅在出现内存不足错误之前替换缩小到 50 像素的库存图片。

所以基本上我正在尝试考虑解决方案。首先,我认为可以通过将原始图像的每 4 个像素(2x2 正方形)平均为一个像素,然后用图像替换这个像素来改善图像效果本身,因为这样小照片在单个打印中会更明显.这也应该允许我以更大的尺寸绘制库存图像。有没有人有这种图像处理的经验?如果是这样,您发现了哪些技巧可以生成漂亮的图像。

最终我认为减少内存错误的方法是重复将图像保存到磁盘并将下一行图像附加到文件中,同时不断地从内存中删除旧的渲染图像集。如何才能做到这一点?是不是和普通的文件追加一样。

我们将不胜感激在这最后一个问题上的任何帮助。

谢谢,

亚历克斯

【问题讨论】:

    标签: java image file-io image-processing


    【解决方案1】:

    这是一个可能有用的建议;

    尝试将两个主要任务分成单独的程序。您的第一个任务是决定哪些图像放在哪里,这可以是从坐标到文件名的简单映射,可以表示为文本行:

    0,0,image123.jpg
    0,1,image542.jpg
    .....
    

    完成该任务后(听起来您处理得很好),然后您可以让单独的程序处理编译。

    可以通过附加到图像来完成此编译,但您可能不想自己弄乱文件格式。最好让您的编程环境使用某种 Java Image 对象来完成它。您可以在内存中容纳的最大像素将是 2GB,导致 sqrt(2x10^9) 最大高度和宽度。从这个数字除以您拥有的高度和宽度的图像数量,您将获得每个子图像允许的整体像素。并且可以将它们绘制到适当的位置。

    【讨论】:

      【解决方案2】:

      我建议研究 Java 高级成像 (JAI) API。您现在可能正在使用 BufferedImage,它确实将所有内容都保存在内存中:源图像以及输出图像。这称为“立即模式”处理。当您调用一个方法来调整图像大小时,它会立即发生。因此,您仍将库存图像保留在内存中。

      使用 JAI,您可以利用两个好处。

      1. 延迟模式处理。
      2. 平铺计算。

      延迟模式意味着当您在图像上调用方法时,输出图像不会被正确计算。相反,调整图像大小的调用会创建一个小的“操作符”对象,该对象可以稍后进行调整大小。这使您可以构建操作链、树或管道。因此,您的工作将为每个库存图像构建一个操作树,例如“裁剪、调整大小、合成”。好的部分是操作只是命令对象,因此您在构建命令时不会消耗所有内存。

      此 API 是基于拉取的。它将计算推迟到某些输出操作 puls 个像素从操作员那里。这可以避免不必要的像素操作,从而快速帮助节省时间和内存。

      例如,假设您需要一个 2048 x 2048 像素的输出图像,从 1600x512 像素的源图像的 512x512 裁剪放大。显然,放大整个 1600x512 的源图像是没有意义的,只是丢掉 2/3 的像素。相反,缩放算子将具有基于其输出维度的“感兴趣区域”(ROI)。缩放算子将 ROI 投影到源图像上,并且只计算那些像素。

      这些命令最终必须得到评估。这发生在少数情况下,主要与最终图像的输出有关。因此,要求 BufferedImage 在屏幕上显示输出将强制所有命令进行评估。同样,将输出图像写入磁盘将强制评估。

      在某些情况下,您可以保留 JAI 的第二个好处,即基于图块的渲染。虽然 BufferedImage 会立即在所有像素上完成所有工作,但平铺渲染一次只对图像的矩形部分进行操作。

      使用之前的示例,2048x2048 输出图像将被分解为图块。假设这些是 256x256,那么整个图像被分成 64 个图块。 JAI 运算符对象知道如何在一个 tile 处工作一个 tile。因此,缩放源图像的 512x512 部分实际上一次在 64x64 源像素上发生 64 次。

      一次计算一个图块意味着遍历图块,这似乎需要更多时间。但是,在进行瓦片计算时,有两件事对您有利。首先,可以同时在多个线程上评估图块。其次,瞬态内存使用量比立即模式计算低得多。

      所有这些都是您为什么要使用 JAI 进行此类图像处理的冗长解释。


      一些注意事项和注意事项:

      1. 您可以在不知不觉中击败基于图块的渲染。在工作流中有 BufferedImage 的任何地方,它都不能充当切片源或接收器。
      2. 如果您使用 JPEG 的 JAI 或 JAI Image I/O 操作符渲染到磁盘,那么您的状态良好。如果您尝试使用 JDK 的内置图像类,您将需要所有内存。 (基本上,避免混合使用两种类型的图像处理。立即模式和延迟模式不能很好地混合。)
      3. 所有带有 ROI、图块和延迟模式的花哨的东西对程序都是透明的。您只需对 JAI 类进行 API 调用。只有当您需要更多地控制磁贴大小、缓存和并发性等事项时,您才需要处理机器。

      【讨论】:

        【解决方案3】:

        每次您“追加”时,您是否可能隐含地创建一个多像素的新对象来替换旧对象(即,与重复追加到字符串而不是使用 StringBuilder 的经典问题类似)?

        如果您发布执行存储和附加的代码部分,有人可能会帮助您找到重新编码的有效方法。

        【讨论】:

          猜你喜欢
          • 2020-10-19
          • 1970-01-01
          • 2012-11-02
          • 1970-01-01
          • 1970-01-01
          • 2015-05-04
          • 1970-01-01
          • 1970-01-01
          • 2015-09-13
          相关资源
          最近更新 更多