Processingとフラグメントシェーダで画像処理を書いてみた

March 21, 2018

フラグメントシェーダとは,CGの描画パイプラインにおいて, ベクタ表現からラスタ表現に変換された画像の各ピクセルに対する処理を行うプログラムであり,3DCGの陰影表現などに用いられる. 本記事では,OpenGL用のシェーダ記述言語であるGLSL (openGL Shading Language) を用いて記述した画像処理の例とその実行結果を紹介する.

フラグメントシェーダに関する詳しい文献

フラグメントシェーダについては『The Book of Shaders』の説明がとてもわかりやすいため,そちらを参照されたい.

The Book of Shaders

https://thebookofshaders.com/?lan=jp

デモ用アプリケーション

Processingで作成した画像処理のデモ用アプリケーションを 以下のリポジトリで公開している. このアプリケーションでは,PCに接続されているカメラから画像を読み込み, 選択中のシェーダによる画像処理を行った結果をウインドウに表示する.

eqs/glsl-image-processing: samples of fragment shader

https://github.com/eqs/glsl-image-processing

アプリケーションは以下の2つのライブラリを使用しているので,実行前にこれらをインストールしておく必要がある:

Processing上でGLSLを実行する際の最小構成

この記事を基にGLSLを記述する際, 上記のデモ用プログラムは自分で改造するには機能が多すぎるかもしれない. そのため,GLSLの実行結果を確認するのに必要な最小限のコードを以下に示す.

import processing.video.*;

final String shaderPath = "01_identity.frag";

PShader sh;
PImage img;
Capture cam;

void setup() {
  size(640, 480, P2D);
  
  String[] cameraList = Capture.list();
  for (int k = 0; k < cameraList.length; k++) {
    println(String.format("%03d : %s", k, cameraList[k]));
  }
  cam = new Capture(this, cameraList[0]);
  cam.start();
  
  sh = loadShader(shaderPath);
}

void draw() {
  background(0);
  
  sh.set("u_time", millis() / 1000.0);
  sh.set("u_resolution", (float)width, (float)height);
  
  if (cam.available()) {
    cam.read();
  }
  
  shader(sh);
  image(cam, 0, 0, width, height);
  resetShader();
}

void mousePressed() {
  sh = loadShader(shaderPath);
}

アプリケーションで実装している各画像処理の実行結果

アプリケーションでは,以下に述べる5つの画像処理を実装している(ひとつは何もしない変換なので実質4つ). このアプリケーションでは画像処理のパラメタ(フィルタサイズや閾値など)を変更するUIは実装していないが, GLSLコード上ではuniform型で変数を宣言してあるため, Processing側にパラメタの設定のコードを追加すれば パラメタを変更した際の実行結果を確認することが可能である.

01_identity.frag : 恒等変換

入力された画像をそのまま出力する変換である. 新しくGLSLによる画像処理を実装する際はこのコードを雛形として用いると良い.

identity

02_grayscale.frag: グレースケール変換

入力された画像をグレースケール化する変換である.

grayscale

03_averaging_filter.frag : 平均化フィルタ (13x13)

入力された画像にたいして平均化フィルタをかける変換である. このコードを改造すれば, ガウシアンフィルタやラプラシアンフィルタも実装可能であると思われる.

averaging

04_chromakey.frag : クロマキー処理

入力された画像上の特定の色について, アルファ値をゼロにする(つまり,完全に透明にする)変換である. このデモ用アプリケーションの初期値は青色を透明化するように設定されている. また,透明化した後の背景には水玉模様がスクロールするアニメーションが表示されるようになっている(このアニメーションはGLSLではなくProcessing側で実装されている).

chromakey

05_duotone.frag : デュオトーン

入力された画像についてデュオトーンによる変換をかける.

duotone

デュオトーンとは,2つの色から作成したグラデーションマップによって 生成されるデザインである. 以下のページにおいて,デザインの例とPhotoshopによる作成方法が述べられている.

超簡単!Photoshopで写真を今どきのデュオトーンに加工する方法(無料グラデーション収録) - PhotoshopVIP

http://photoshopvip.net/93748

05_duotone.fragにおいては,この変換を次のようにして作成している:

  1. グラデーションマップの生成に使用する2つの色を用意する
    • プログラム中のcolorAおよびcolorBがこれに相当する
  2. 入力画像をグレースケール変換する
  3. GLSLのmix関数によってcolorAからcolorBに変化するグラデーションマップを作成し,グレーのグラデーションマップから新しいグラデーションマップへのマッピングを行う

グレーからカラーへのマッピングの対応は以下の図に示す通りで, 暗い色は青色に近い色に,明るい色は黄色に近い色にマッピングされる.

gradation

まとめ

本記事では,GLSLによって記述した画像処理をProcessing上で 実行した結果を述べた. 今回は簡単な画像処理のみを実装したが,バイラテラルフィルタやLine Integral Convolutionなどのクリエイティブコーディングにおいて有用な画像処理も 実装可能であると考えられる. また,Processingの代わりにopenFrameworksで実行して強力なアドオンと組み合わせるのも良いと思われる.