2010-12-06 103 views
5

我写了一个程序,需要一个'照片',并为每个像素选择插入一系列其他照片的图像。所选图像是平均颜色与照片中最接近原始像素的照片。附加到图像文件

我已经完成了这一工作,首先对'股票'图像中每个像素的RGB值进行平均,然后将其转换为CIE LAB,这样我就可以计算出它在人类感知方面的“接近”的颜色。

我已经编辑了一幅图像,其中原始“照片”图像中的每个像素已被替换为“最接近”的图像。

它工作得很好,效果很好,但是股票图像的大小是300×300像素,甚至是“-Xms2048m -Xmx2048m”的虚拟机标志,这是我知道的嘲笑,在555px×540px图像I只能在我得到内存不足错误之前将库存图像缩小到50 px。

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

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

任何帮助在这最后的事情将不胜感激。

感谢,

亚历

回答

3

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

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

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

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

此API是基于拉的。它延迟计算,直到某些输出动作从操作员处拉出像素。这可以避免无用的像素操作,从而快速节省时间和内存。

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

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

在某些情况下,您可以保留基于图块渲染的JAI的第二个好处。尽管BufferedImage在所有像素中都立即执行了所有工作,但瓦片渲染一次只对图像的矩形部分进行操作。

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

每次计算一个tile意味着在tile之间循环,这似乎需要更多时间。然而,在进行瓦片计算时,有两件事对你有利。首先,瓷砖可以同时在多个线程上评估。其次,瞬时内存使用量远远低于即时模式计算。

所有这些都是你为什么要使用JAI来处理这类图像的长篇大论。


有两点要注意和警告:

  1. 你能打败基于区块的渲染没有意识到这一点。无论你在工作流中获得BufferedImage,它都不能用作瓦片源或接收器。
  2. 如果使用JPEG的JAI或JAI图像I/O操作符渲染到磁盘,那么您的状态良好。如果您尝试使用JDK的内置图像类,则需要全部内存。 (基本上,避免混合两种类型的图像处理,即时模式和延迟模式不能很好地混合。)
  3. 所有具有感兴趣区域,切片和延迟模式的花哨材料对程序都是透明的。您只需在JAI类上进行API调用。如果您需要更多控制瓦片大小,缓存和并发等功能,则只需处理机器。
0

每次你追加'你也许隐式创建一个多个像素的新对象,以取代旧的(即平行于反复追加的经典问题到一个字符串,而不是使用StringBuilder)?

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

2

下面是一个可能有用的建议;

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

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

(这听起来像你处理得好),那么你可以有一个单独的程序处理编译。

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