Sid*_*ina 19 programming-languages functional-programming scala image-processing
我们正在为Scala(学生项目)开发一个小型图像处理库.该库完全正常(即没有可变性).存储图像的光栅Stream[Stream[Int]]
以便以最少的努力利用惰性评估的益处.但是,在对映像执行一些操作时,堆会变满并OutOfMemoryError
抛出一个.(例如,在JVM堆耗尽空间之前,最多可以对大小为500 x 400的jpeg映像执行4次操作,35 kb.)
我们想到的方法是:
Stream[Stream[Int]]
,更适合图像处理的任务.(同样,我们对简单List
和以外的功能数据结构没有太多了解Stream
.)我们的最后一个选择是放弃不变性并使其成为一个可变的库(如流行的图像处理库),我们并不是真的想做.如果你知道我的意思,请建议我们一些方法来保持这个库的功能和功能.
谢谢,
Siddharth Raina.
ADDENDUM:
对于大小为1024 x 768的映像,即使对于单个映射操作,JVM也会耗尽堆空间.我们测试的一些示例代码:
val image = Image from "E:/metallica.jpg"
val redded = image.map(_ & 0xff0000)
redded.display(title = "Redded")
Run Code Online (Sandbox Code Playgroud)
并输出:
"C:\Program Files (x86)\Java\jdk1.6.0_02\bin\java" -Didea.launcher.port=7533 "-Didea.launcher.bin.path=C:\Program Files (x86)\JetBrains\IntelliJ IDEA Community Edition 10.0.2\bin" -Dfile.encoding=windows-1252 -classpath "C:\Program Files (x86)\Java\jdk1.6.0_02\jre\lib\charsets.jar;C:\Program Files (x86)\Java\jdk1.6.0_02\jre\lib\deploy.jar;C:\Program Files (x86)\Java\jdk1.6.0_02\jre\lib\javaws.jar;C:\Program Files (x86)\Java\jdk1.6.0_02\jre\lib\jce.jar;C:\Program Files (x86)\Java\jdk1.6.0_02\jre\lib\jsse.jar;C:\Program Files (x86)\Java\jdk1.6.0_02\jre\lib\management-agent.jar;C:\Program Files (x86)\Java\jdk1.6.0_02\jre\lib\plugin.jar;C:\Program Files (x86)\Java\jdk1.6.0_02\jre\lib\resources.jar;C:\Program Files (x86)\Java\jdk1.6.0_02\jre\lib\rt.jar;C:\Program Files (x86)\Java\jdk1.6.0_02\jre\lib\ext\dnsns.jar;C:\Program Files (x86)\Java\jdk1.6.0_02\jre\lib\ext\localedata.jar;C:\Program Files (x86)\Java\jdk1.6.0_02\jre\lib\ext\sunjce_provider.jar;C:\Program Files (x86)\Java\jdk1.6.0_02\jre\lib\ext\sunmscapi.jar;C:\Program Files (x86)\Java\jdk1.6.0_02\jre\lib\ext\sunpkcs11.jar;C:\new Ph\Phoebe\out\production\Phoebe;E:\Inventory\Marvin.jar;C:\scala-2.8.1.final\lib\scala-library.jar;C:\scala-2.8.1.final\lib\scala-swing.jar;C:\scala-2.8.1.final\lib\scala-dbc.jar;C:\new Ph;C:\scala-2.8.1.final\lib\scala-compiler.jar;E:\Inventory\commons-math-2.2.jar;E:\Inventory\commons-math-2.2-sources.jar;E:\Inventory\commons-math-2.2-javadoc.jar;E:\Inventory\jmathplot.jar;E:\Inventory\jmathio.jar;E:\Inventory\jmatharray.jar;E:\Inventory\Javax Media.zip;E:\Inventory\jai-core-1.1.3-alpha.jar;C:\Program Files (x86)\JetBrains\IntelliJ IDEA Community Edition 10.0.2\lib\idea_rt.jar" com.intellij.rt.execution.application.AppMain phoebe.test.ImageTest
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at scala.collection.Iterator$class.toStream(Iterator.scala:1011)
at scala.collection.IndexedSeqLike$Elements.toStream(IndexedSeqLike.scala:52)
at scala.collection.Iterator$$anonfun$toStream$1.apply(Iterator.scala:1011)
at scala.collection.Iterator$$anonfun$toStream$1.apply(Iterator.scala:1011)
at scala.collection.immutable.Stream$Cons.tail(Stream.scala:565)
at scala.collection.immutable.Stream$Cons.tail(Stream.scala:557)
at scala.collection.immutable.Stream$$anonfun$map$1.apply(Stream.scala:168)
at scala.collection.immutable.Stream$$anonfun$map$1.apply(Stream.scala:168)
at scala.collection.immutable.Stream$Cons.tail(Stream.scala:565)
at scala.collection.immutable.Stream$Cons.tail(Stream.scala:557)
at scala.collection.immutable.Stream$$anonfun$flatten1$1$1.apply(Stream.scala:453)
at scala.collection.immutable.Stream$$anonfun$flatten1$1$1.apply(Stream.scala:453)
at scala.collection.immutable.Stream$Cons.tail(Stream.scala:565)
at scala.collection.immutable.Stream$Cons.tail(Stream.scala:557)
at scala.collection.immutable.Stream.length(Stream.scala:113)
at scala.collection.SeqLike$class.size(SeqLike.scala:221)
at scala.collection.immutable.Stream.size(Stream.scala:48)
at scala.collection.TraversableOnce$class.toArray(TraversableOnce.scala:388)
at scala.collection.immutable.Stream.toArray(Stream.scala:48)
at phoebe.picasso.Image.force(Image.scala:85)
at phoebe.picasso.SimpleImageViewer.<init>(SimpleImageViewer.scala:10)
at phoebe.picasso.Image.display(Image.scala:91)
at phoebe.test.ImageTest$.main(ImageTest.scala:14)
at phoebe.test.ImageTest.main(ImageTest.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:115)
Process finished with exit code 1
Run Code Online (Sandbox Code Playgroud)
axe*_*l22 19
如果我理解正确,您将每个像素存储在一个Stream
元素中,这可能效率低下.你可以做的是创建你的自定义LazyRaster
类,它包含对某个大小的图像块的惰性引用(例如,20x20).第一次写入某个块时,会初始化其相应的数组,并且从那里更改像素意味着写入该数组.
这是更多的工作,但可能会带来更好的性能.此外,如果您希望支持图像操作的堆叠(例如,执行地图 - 拍摄 - 地图),然后"一次性"评估图像,实施可能会变得棘手 - 流实施是最好的证据.
人们可以做的另一件事是确保老旧Stream
的垃圾收集得当.我怀疑image
你的例子中的对象是你的流的包装器.如果您希望将多个图像操作(如映射)叠加在一起并且能够gc不再需要的引用,则必须确保不保留对流的任何引用 - 请注意,如果出现以下情况,则无法确保:
image
在示例中)Image
包装器包含这样的引用.如果不了解更多关于确切用例的话,很难说更多.
就个人而言,我会Stream
完全避免使用s,并简单地使用一些基于数组的不可变数据结构,这种数据结构既节省空间又避免装箱.我可能看到Stream
被使用的唯一地方是迭代图像变换,例如卷积或应用一叠滤波器.你不会有一个Stream
像素,而是一个Stream
图像.这可能是表达一系列转换的好方法 - 在这种情况下,上面给出的链接中关于gc的注释适用.
ret*_*nym 12
如果处理大型流,则需要避免保持对流的头部的引用.这样可以防止垃圾回收.
调用某些方法可能Stream
会在内部保持不变.请参阅此处的讨论:没有OutOfMemory错误的Scala流的功能处理
我建议还要考虑连续而不仅仅是离散的图像模型.连续通常比离散更加模块化/可组合 - 无论是时间还是空间.
流更多是关于懒惰评估而不是不可变性.而且你这样做会迫使每个像素产生大量的空间和时间开销.此外,只有当您可以推迟确定(计算或检索)各个像素值时,Streams才有意义.当然,随机访问是不可能的.我不得不认为Stream是一个完全不合适的图像处理数据结构.
我强烈建议您管理自己的光栅内存(不将单个光栅图像组织固定到代码中的奖励点),并为整个通道或平面或其中的频段分配存储空间(取决于正在播放的光栅组织).
更新:通过上述内容,我的意思是不使用嵌套数组或IndexedSeq,而是分配一个块并使用行和列值计算哪个元素.
然后采取"初始化后不可变"的方法.一旦在栅格中建立了给定像素或样本,就永远不允许更改它.这可能需要一位光栅平面来跟踪已建立的像素.或者,如果您知道如何填充栅格(将分配像素的顺序),您可以更简单,更便宜地表示建立了多少栅格以及需要填充多少栅格.
然后,当您对光栅图像执行处理时,在没有图像被更改的管道中执行此操作,而是在应用各种变换时始终生成新图像.
您可能会考虑对某些图像转换(例如卷积),您必须采用这种方法,否则您将无法获得正确的结果.