Tips : Processing PImageのメモリリーク対策

ProcessingのPImageには、メモリリークの問題があるので、その回避方法を書きとめておきます。ちなみに、僕の環境ではProcessing 2.1を利用しています。

通常loadImage()などでsetup()内から1度呼ぶくらいであればリークすることはないと思いますが、 draw()の中でcreateImage()などを実行しているようなケースでは、PImageを作るたびにメモリが確保され続け、いずれExceptionしてしまいます。 僕が書いていたコードを例に、回避の仕方をまとめます。

カメラキャプチャイメージを集めて利用するケーススタディ

例えばカメラからの動画をずっと貼り付けておくのではなく、特定フレーム毎に静止画としてカメラキャプチャ画像を取得して、画像としてを利用するケースを考えます。
少し省略しますが、下記のように毎秒1フレームでキャプチャを取得してArrayListにスタックすることを考えます。

import processing.video.*;
Capture cam;
ArrayList<PImage> imgs;

void setup(){
    frameRate(1);
    String[] cams = Capture.list();
    if(cams.length == 0) {
        exit();
    } else {
        cam = new Capture(this,cams[0]);
        cam.start();
    }

    imgs = new ArrayList<PImage>();
}

void draw(){

    if (cam.available() == true) {
        cam.read();
    }

    PImage tmp = cam.get();
    imgs.add(tmp);
}

この例だと、実行して放置するとExceptionしてしまいます。 これを回避する方法はGithubのIssueのスレッドにあるのですが、 g.removeCache(image)を追記するのが有効です。

void draw(){

    if (cam.available() == true) {
        cam.read();
    }

    PImage tmp = cam.get();
    imgs.add(tmp);
    g.removeCache(tmp);
}

さらに、ArrayListを空にするタイミングでも、実行する必要があります。

void draw(){

    if (cam.available() == true) {
        cam.read();
    }

    if (imgs.size() > 10) {
        for (int n = 0; n < imgs.size(); n++) {
            g.removeCache(imgs.get(n));
        }
        imgs.clear();
        System.gc();
        println("cleared buffer size: "+ imgs.size());
    }

    PImage tmp = cam.get();
    imgs.add(tmp);
    g.removeCache(tmp);
}

System.gc()も加えていますが、これだけだと回避できませんでした。
なのでArrayListに入っているPImageの参照ひとつひとつにremoveCacheをする必要があります。