Processingで描画した作品を範囲を取得して画像として保存する



Processingで作成したデザインやアートを作成していく中で、出来た作品を画像として保存したいことがあります。

ただウィンドウ画面の表示を画像として保存するには、Processingで用意されているsave関数を使うだけで実装できます。

save関数の構文

save(filename)


ですが、ウィンドウの表示全体ではなく、ウィンドウの表示の一部分を切り取って画像として保存したいこともあるでしょう。

ここでは、Processingで描画した作品を範囲を決めて画像として保存する方法についてご紹介します。

範囲を決めて画像として保存する


サンプルとして、簡単なお絵描きツールのようなものを作ってみました。

save関数を使うことでウィンドウの表示を画像として保存することができますが、画像として保存するのに必要のないボタンの部分を省きたい場合には、保存する部分を切り取る必要があります。

以下、サンプルコードになります。

void setup(){
  size(800, 420);
  pixelDensity(1);
  smooth();
  background(255);
  strokeWeight(8);
  textSize(20);
}

void draw() {
  
  if (mousePressed) {
    stroke(0);
    line(pmouseX, pmouseY, mouseX, mouseY);
  }

  fill(120);
  noStroke();
  rect(0, height-60, width, 60);
  
  pushMatrix();
  translate(width-90, height-50);
  fill(200);
  noStroke();
  rect(0, 0, 80, 40);
  fill(0);
  text("Clear", 15, 27);
  popMatrix();
}

void mouseClicked() {
  if(mouseY >= height-50 && mouseY <= height-10) {
    if(mouseX >= width-90 && mouseX <= width-10) {
      background(255);
    }
  }
}

void keyPressed() {

  if (key == 's' || key == 'S') {

    // 画面の上部分(メニューを除く範囲)だけを直接切り抜く
    // get(X座標, Y座標, 横幅, 縦幅)
    PImage img = get(0, 0, width, height-60);

    // 切り抜いた画像(img)を保存する
    img.save("save_images/drawing.png");

  }
 
}



描くエリアがあり、下の部分にはクリアするボタンを設置して描いたものを消せるようにしています。
試しに適当になにか描いてみます。

Processingのドローイングツールで描画する


保存の方法として、void keyPressed でキーボードが押された時に実行される関数を用意して、if分の条件分岐で「s」のキーが押されたら画像として保存するプログラムを実行するようにします。
小文字だけでなく大文字にも対応する場合は if(key == 's' || key == 'S') としてもらえばOKです。

まずは、画像として保存したい範囲をget関数を使って取得します。
get関数は、画面上の指定した領域を切り出して、画像データ(PImage)として読み込む関数です。

get関数の構文

get(x, y, width, hegiht)


今回は以下のようにパラメータを指定しています。

0, 0: 切り抜くスタート位置(画面の左上端)
width: 横幅(画面の端から端まで)
height - 60: 縦幅(画面の一番下からメニューの高さ60ピクセル分を引いた位置まで)



今回のサンプルの場合は、描く部分だけを範囲として切り出します。
そして、切り抜いた画像(img)をsave関数を使って、ディレクトリやファイル名を設定し、画像として保存します。

実際の描いたものを保存してみます。

キーボードの「s」キーを押して画像として保存。
保存された画像はスケッチファイルと同じフォルダ(指定したディレクトリ)に保存されます。

save関数で保存された画像は、スケッチファイルと同じフォルダ(指定したディレクトリ)に保存される



今回のサンプルは、sキーを押すたびに画像として保存していきますので、キーを押すたびに画像が更新されるかと思います。

ただただウィンドウの表示を画像として保存する場合はsave関数だけを利用すればいいですが、画像として保存する範囲を決めるのであれば、get関数をうまく使うことで可能となります。


注意点

MacのRetinaディスプレイやWindowsの高解像度モニターなどでは、画面をきれいに見せるために内部で縦横2倍(計4倍)のピクセル数で処理されています。

この影響で、size(800, 420) と指定していても内部データは 1600 x 840 になるため、get(0, 0, 800, 360) を実行すると内部データの左上1/4だけが切り出され、画像が拡大されて保存されてしまいます。

そのために setup() 内では pixelDensity(1); を記述しておきましょう。ピクセル倍率を1倍に固定することで、どんなモニターでも指定通りの範囲を正しく get() で保存できるようになります。

タイムスタンプ付きファイル名の生成

save関数で画像として保存する中で、生成するファイルの名前が固定ですと、2回、3回と何度か保存していく場合に画像ファイルが上書きされてしまい、前のデータが無くなってしまいます。
保存するたびにファイル名が変更できる方が使い勝手がいいでしょう。そこでファイル名の末尾に現在の日時としてタイムスタンプを付けるのがおすすめです。

以下の関数を使うことで、日付の情報を取得することができます。

year() : 現在の「年」
month() : 現在の「月」
day() : 現在の「日」
hour() : 現在の「時」
minute() : 現在の「分」
second() : 現在の「秒」


以下、先ほどの保存を実装する部分のブログラムに修正を加えます。

void keyPressed() {

  if (key == 's' || key == 'S') {

    // 画面の上部分(メニューを除く範囲)だけを直接切り抜く
    // get(X座標, Y座標, 横幅, 縦幅)
    PImage img = get(0, 0, width, height-60);

    // 現在の日時を取得してタイムスタンプ文字列を作る
    String timestamp = nf(year(), 4) + nf(month(), 2) + nf(day(), 2) + "_" +
                       nf(hour(), 2) + "-" + nf(minute(), 2) + "-" + nf(second(), 2);

    // 切り抜いた画像(img)をタイムスタンプ付きのファイル名で保存する
    img.save("save_images/drawing_" + timestamp + ".png");

  }
 
}


nf関数は、指定した桁数になるように 0 で埋めて形を揃える関数です。これによって綺麗に表示を整えることができます。

nf関数の構文

nf(数値, 桁数)
Processingの描画をタイムスタンプ付きのファイル名をつけて画像として生成する


このように、現在の「年・月・日・時・分・秒」を取得してファイル名に組み込む(タイムスタンプを付ける)ことで、保存する際に重複しないファイル名が自動で作られ、上書きを防ぐことができます。

Processingで作った作品などを画像として保存、またその範囲を決めたい場合は今回の方法を試してみてください。
合わせて、タイムスタンプ付きのファイルを生成できるようにすると、画像データの管理がしやすくなるでしょう。