Processingにおけるの画像(PImage)のコピー
【2016/10/25修正】
Processingにおいて画像はPImage型で扱われます。特に画像処理をやるようなケースにおいて、画像のコピーを行いたいことが多々あります。その場合に注意すべきことをここに書いておきます。
PImageのコピー
画像をコピーしたいときうっかり以下のようにやりがちですが、よく考えずにこれをやるとちょっと困ったことになります。
PImage src = loadImage("lenna.jpg"); // srcにファイルから画像を読み込み PImage dst = src; // srcをdstにコピーしたつもり
「dst = src」でコピーされるのはデータの実体ではなく、データがメモリ上のどこにあるのかという参照値です。つまりdstもsrcもメモリ上で同じデータを参照していることになるので、このあとにdstに対してピクセルデータを書きかえる処理を行うと、srcのピクセルデータも書き変わってしまいます。
PImage型のデータのコピーを作りたいときは、get()というメソッドを使います(Processing3以降はcopy()でも可)。
PImage src = loadImage("lenna.jpg"); // srcにファイルから画像を読み込み PImage dst = src.get(); // srcのコピー(新しく生成)をdstに代入
2行目が意味するところは「srcをコピーして新しいオブジェクトを生成し、そこへの参照をdstという変数に入れる」です。ここで注意すべきは、dstという変数にもともと別な値(別なオブジェクトへの参照)が入っていた場合には、それが代入によって失われるという点です。
PImage型変数間でのピクセルデータのコピー
2つのPImage型の変数があって、その一方から他方に向かってピクセルデータのみをコピーしたい場合はset()を使うとよいです。
PImage src = loadImage("lenna.jpg"); // srcにファイルから画像を読み込み PImage dst = createImage( src.width, src.height, RGB ); // srcと同じサイズの画像を作成 dst.set( 0, 0, src ); // dstにsrcのピクセルデータを書き込み。0,0はオフセット。
PImage型の引数を持つ関数を作る場合
もうひとつ例をみておきましょう。以下のコードは「copyImage()という自作の関数を用いて、img1のピクセルデータをimg2にコピーしたい」という意図で書かれたものですが、これはプログラマの期待どおりに動作しません。
PImage img1; PImage img2; void setup() { size( 800, 600 ); img1 = loadImage("lenna.jpg"); // ファイルから画像を読み込み img2 = createImage( img1.width, img1.height, RGB ); // img1と同サイズの空の画像を作成 } void draw() { // img1のピクセルデータをimg2にコピーした(つもり) copyImage( img1, img2 ); // img2を画面描画。img1と同じ画像が表示されるはずなんだけど、表示されない image( img2, 0, 0 ); } // 入力画像(img_in)を出力画像(img_out)にコピーする関数(のつもり) void copyImage( PImage img_in, PImage img_out ) { img_out = img_in.get(); }
copyImage()という関数内で行われるのは、img_in.get()でコピーされて作られた新しいオブジェクトへの参照を、img_outという変数に入れるという処理なので、呼び出し元の変数img2の画像にはなんの影響ももたらしません。
プログラマの意図は「img1のピクセルデータをimg2にコピーしたい」ということなので、以下のように書けば期待通り動作します。
void copyImage( PImage img_in, PImage img_out ) { img_out.set( 0, 0, img_in ); }