Java/Scala で風景から歩行者を消してみる

一昨日くらいにホッテントリ入りしてた記事↓を見て、

Export["result.jpg",
 Image[Mean[Map[ImageData,
    Import["movie.mov", "ImageList"]]]]]

このくらいのコードで済むなら Java/Scala でもすぐに書けるかも? と思ってやってみた。

理想

ヤッター、こんなに簡単にできたよー^^

import opencv._                               // ← ん?
System.loadLibrary(Core.NATIVE_LIBRARY_NAME)  // ← んんん???
saveImage("result.jpg", loadVideo("movie.mov")(mean))

現実

Isolator requires Java bindings for OpenCV. 

$ curl -OL https://github.com/Itseez/opencv/archive/2.4.9.zip -o opencv-2.4.9.zip
$ unzip opencv-2.4.9.zip
$ cd opencv-2.4.9
$ mkdir build
$ cd build/
$ cmake -DBUILD_SHARED_LIBS=OFF ..
$ make -j8
import org.opencv.core.{Core, CvType, Mat, Scalar, Size}
import org.opencv.highgui.{Highgui, VideoCapture}

package object opencv {
  ...
  def mean(frames: Iterator[Mat]): Mat =
    if (frames.hasNext) {
      val head = convertTo(CvType.CV_64FC3)(frames.next)
      val (count, out) =
        frames.
          map(convertTo(CvType.CV_64FC3)).
          foldLeft((1, head)) { case ((cnt, sum), f) => (cnt + 1, add(sum, f)) }

      divide(out, new Scalar(count, count, count))
    } else {
      new Mat
    }

  def loadVideo[A](filename: String)(f: Iterator[Mat] => A): A = {
    val cap = new VideoCapture(filename)
    try {
      f(Iterator.continually(nextFrame(cap)).takeWhile(_ != None).map(_.get))
    } finally {
      cap.release()
    }
  }
  ...
  def saveImage(filename: String, m: Mat) = Highgui.imwrite(filename, m)
}

^^;;;

考察

https://raw.githubusercontent.com/okapies/isolator/master/examples/mean-opencv.jpg https://raw.githubusercontent.com/okapies/isolator/master/examples/mean-opencv.jpg

Mathematica、画像や動画の読み書きをネイティブサポートしているのはホントにいいなぁ、という感想。

なんか、動画を扱える Pure Java の成熟したソリューションって未だにあんまりないらしくて、したがって OpenCVJava から叩くのが一番堅実な選択肢ということになり、その道の先には jar をソースからビルドする楽しい作業が待ってたり、コーディング時もリソースの明示的な解放をサボると一瞬でメモリが爆裂したりと、色々めんどくさい…。

コード

今回書いたコードは GitHub に置いておきますので、世紀末ごっこして遊ぶなり改造するなりどうぞ。

あと、mean のアルゴリズムはもう少しマシな方法があるように思うので、OpenCV に詳しい方、どなたかご教示くださいませ…。

追記

@colspan一晩でやってくれました