附加到图像文件

时间:2010-12-06 00:54:17

标签: java image file-io image-processing

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

我通过首先平均“stock”图像中每个像素的rgb值然后将其转换为CIE LAB来完成此操作,这样我就可以根据人类感知来计算它与所讨论像素的“接近”程度。颜色。

然后我编辑了一张图片,原始“照片”图片中的每个像素都被“最近”的图片替换。

效果很好而且效果很好但是库存图像大小是300 x 300像素,甚至虚拟机标志为“-Xms2048m -Xmx2048m”,我知道这是荒谬,555像素×540像素我可以在我出现内存不足错误之前,只能将缩小到50像素的图像替换掉。

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

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

非常感谢在这最后一件事情上的任何帮助。

谢谢,

亚历

3 个答案:

答案 0 :(得分:3)

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

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

  1. 延迟模式处理。
  2. 平铺计算。
  3. 延迟模式意味着当您在图像上调用方法时,不会立即计算输出图像。相反,调用图像大小的调用会创建一个小的“操作符”对象,以后可以进行调整大小。这使您可以构建链,树或操作管道。因此,您的工作将为每个库存图像构建一个操作树,例如“裁剪,调整大小,复合”。好的部分是操作只是命令对象,因此在构建命令时不会占用所有内存。

    此API基于拉式。它推迟计算,直到某些输出动作从运算符中拉出像素。通过避免不必要的像素操作,这可以快速帮助节省时间和内存。

    例如,假设您需要一个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. 所有具有投资回报率,图块和延迟模式的花哨内容对程序都是透明的。您只需在JAI类上进行API调用。如果您需要更多地控制磁贴大小,缓存和并发等内容,则只需处理机器。

答案 1 :(得分:2)

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

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

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

在完成任务之后(听起来你已经很好地处理了它),那么你可以有一个单独的程序来处理编译。

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

答案 2 :(得分:0)

每次你'追加'时,你可能隐含地创建了一个带有一个像素的新对象来替换旧的对象(即,与重复附加到String而不是使用StringBuilder的经典问题并行)?

如果您发布代码中用于存储和追加的部分,则有人可能会帮助您找到有效的重新编码方式。