ライブラリの利用(OpenCV for Processing)

      ライブラリの利用(OpenCV for Processing) はコメントを受け付けていません

このページでは、Processingに機能を追加する「ライブラリ」の使い方を学びます。ここでは画像処理やコンピュータビジョンのためのライブラリである OpenCV for Processing を使います。

OpenCV for Processingを使うと、カメラ映像に対して、明るさ・コントラストの変更、エッジ検出、二値化、ぼかし、顔検出などを行うことができます。p5.jsの実行欄では、ブラウザで再現できる範囲の画像処理を実行します。

カメラはまだ開始されていません。

1. OpenCV for Processingのインストール

Processingのメニューから、Sketch > Import Library… > Add Library… を選び、OpenCV for Processing を検索してインストールします。

インストールできたら、スケッチの最初に次のように書いてライブラリを読み込みます。

import gab.opencv.*;
import processing.video.*;
import java.awt.*;

2. カメラ映像の表示

まずは、カメラ映像を表示します。OpenCVを使う前に、カメラから映像を取得できるか確認します。

リスト1
import gab.opencv.*;
import processing.video.*;
import java.awt.*;

Capture video;
OpenCV opencv;

void setup() {
  size(640, 480);

  String[] cameras = Capture.list();

  // カメラが認識されるまで待つ
  while (cameras.length == 0) {
    cameras = Capture.list();
  }

  // 最初に見つかったカメラを使う
  video = new Capture(this, 640, 480, cameras[0]);

  // OpenCVで640×480の画像を扱う
  opencv = new OpenCV(this, 640, 480);

  video.start();
}

void draw() {
  // カメラ映像をOpenCVに読み込む
  opencv.loadImage(video);

  // カメラ映像をそのまま表示する
  image(video, 0, 0, width, height);
}

void captureEvent(Capture c) {
  c.read();
}
実行結果:カメラ映像をそのまま表示する

3. 明るさとコントラスト

明るさは画面全体を明るくしたり暗くしたりする処理です。コントラストは、明るい部分と暗い部分の差を強くしたり弱くしたりする処理です。

リスト2
import gab.opencv.*;
import processing.video.*;
import java.awt.*;

Capture video;
OpenCV opencv;

void setup() {
  size(640, 480);

  String[] cameras = Capture.list();
  while (cameras.length == 0) {
    cameras = Capture.list();
  }

  video = new Capture(this, 640, 480, cameras[0]);
  opencv = new OpenCV(this, 640, 480);
  video.start();
}

void draw() {
  opencv.loadImage(video);

  // mouseXが左端なら-255、右端なら255になる
  // つまり、マウスの横位置で明るさを変える
  int b = int(map(mouseX, 0, width, -255, 255));
  opencv.brightness(b);

  // コントラストを試したい場合はこちらを使う
  // float c = map(mouseX, 0, width, 0.0, 5.0);
  // opencv.contrast(c);

  image(opencv.getOutput(), 0, 0, width, height);
}

void captureEvent(Capture c) {
  c.read();
}
明るさコントラスト
実行結果:明るさとコントラストを変更する

4. エッジの検出

エッジ検出は、画像の中で色や明るさが大きく変化している場所を取り出す処理です。輪郭線のような画像を作るときに使います。

リスト3
import gab.opencv.*;
import processing.video.*;
import java.awt.*;

Capture video;
OpenCV opencv;

void setup() {
  size(640, 480);

  String[] cameras = Capture.list();
  while (cameras.length == 0) {
    cameras = Capture.list();
  }

  video = new Capture(this, 640, 480, cameras[0]);
  opencv = new OpenCV(this, 640, 480);
  video.start();
}

void draw() {
  opencv.loadImage(video);

  // Cannyエッジ検出
  // 50より弱い変化は無視し、200より強い変化はエッジとして扱う
  opencv.findCannyEdges(50, 200);

  image(opencv.getOutput(), 0, 0, width, height);
}

void captureEvent(Capture c) {
  c.read();
}
実行結果:エッジ検出風の処理。明るさの差が大きい場所を線として表示する

5. 二値化

二値化は、画像を白と黒の2色に分ける処理です。明るさがしきい値より大きければ白、小さければ黒にします。

リスト4
import gab.opencv.*;
import processing.video.*;
import java.awt.*;

Capture video;
OpenCV opencv;

void setup() {
  size(640, 480);

  String[] cameras = Capture.list();
  while (cameras.length == 0) {
    cameras = Capture.list();
  }

  video = new Capture(this, 640, 480, cameras[0]);
  opencv = new OpenCV(this, 640, 480);
  video.start();
}

void draw() {
  opencv.loadImage(video);

  // しきい値50で白黒に分ける
  opencv.threshold(50);

  image(opencv.getOutput(), 0, 0, width, height);
}

void captureEvent(Capture c) {
  c.read();
}
しきい値
実行結果:しきい値で白黒2色に分ける

6. ぼかし

ぼかしは、周囲のピクセルの色を混ぜて、画像をなめらかにする処理です。ノイズを減らしたり、柔らかい印象にしたりできます。

リスト5
import gab.opencv.*;
import processing.video.*;
import java.awt.*;

Capture video;
OpenCV opencv;

void setup() {
  size(640, 480);

  String[] cameras = Capture.list();
  while (cameras.length == 0) {
    cameras = Capture.list();
  }

  video = new Capture(this, 640, 480, cameras[0]);
  opencv = new OpenCV(this, 640, 480);
  video.start();
}

void draw() {
  opencv.loadImage(video);

  // 値を大きくすると、ぼかしが強くなる
  opencv.blur(12);

  image(opencv.getOutput(), 0, 0, width, height);
}

void captureEvent(Capture c) {
  c.read();
}
ぼかし
実行結果:映像をぼかす

7. 顔の検出

OpenCV for Processingでは、顔検出用のデータを読み込むことで、顔の位置を四角形で囲むことができます。OpenCVには顔以外にも、目、鼻、口などを検出するためのデータがあります。

リスト6
import gab.opencv.*;
import processing.video.*;
import java.awt.*;

Capture video;
OpenCV opencv;

void setup() {
  size(640, 480);

  String[] cameras = Capture.list();
  while (cameras.length == 0) {
    cameras = Capture.list();
  }

  video = new Capture(this, 640, 480, cameras[0]);
  opencv = new OpenCV(this, 640, 480);

  // 顔検出用のデータを読み込む
  opencv.loadCascade(OpenCV.CASCADE_FRONTALFACE);

  video.start();
}

void draw() {
  // 640×480の映像をそのまま640×480で表示する

  opencv.loadImage(video);
  image(video, 0, 0, width, height);

  Rectangle[] faces = opencv.detect();

  noFill();
  stroke(0, 255, 0);
  strokeWeight(3);

  for (int i = 0; i < faces.length; i++) {
    rect(faces[i].x, faces[i].y, faces[i].width, faces[i].height);
  }
}

void captureEvent(Capture c) {
  c.read();
}
実行結果:ブラウザがFaceDetector APIに対応している場合、顔を緑の四角で囲む

8. 動きの検出

前のフレームと現在のフレームを比べると、動いている部分を取り出すことができます。これは「フレーム差分」と呼ばれる考え方です。ここではOpenCVのdiff()threshold()を使います。背景があまり動かない状態で、人や手だけが動くと、その部分だけが白く表示されます。

リスト7
import gab.opencv.*;
import processing.video.*;
import java.awt.*;

Capture video;
OpenCV opencv;
PImage previousFrame;

void setup() {
  size(640, 480);

  String[] cameras = Capture.list();
  while (cameras.length == 0) {
    cameras = Capture.list();
  }

  video = new Capture(this, 640, 480, cameras[0]);
  video.start();

  opencv = new OpenCV(this, 640, 480);

  // 前のフレームを保存しておくための画像
  previousFrame = createImage(width, height, RGB);
}

void draw() {
  if (video.available()) {
    video.read();
  }

  // 現在のカメラ映像をOpenCVに読み込む
  opencv.loadImage(video);

  // 現在の映像と前のフレームの差分を計算する
  // 動いている部分ほど明るくなる
  opencv.diff(previousFrame);

  // 差分を白黒に分ける
  // 値を小さくすると小さな動きも検出しやすくなる
  opencv.threshold(40);

  image(opencv.getOutput(), 0, 0, width, height);

  // 次のdraw()で比較するため、現在の映像を保存する
  previousFrame.copy(video, 0, 0, video.width, video.height, 0, 0, width, height);
}
実行結果:前フレームとの差分で、動いている部分だけを白く表示する

この処理は、カメラの前で手を動かすと効果がわかりやすくなります。背景が静止しているほど、動いている部分だけが強調されます。

9. RGBチャンネルをずらす

映像の赤・緑・青の成分を少しずつずらして表示すると、色収差やグリッチのような効果になります。ここではOpenCVのgetR()getG()getB()で色のチャンネルを取り出します。画像処理の基本である「色の成分を分けて扱う」ことが、視覚的にわかりやすいサンプルです。

リスト8
import gab.opencv.*;
import processing.video.*;
import java.awt.*;

Capture video;
OpenCV opencv;

PImage redImg;
PImage greenImg;
PImage blueImg;

void setup() {
  size(640, 480);

  String[] cameras = Capture.list();
  while (cameras.length == 0) {
    cameras = Capture.list();
  }

  video = new Capture(this, 640, 480, cameras[0]);
  video.start();

  opencv = new OpenCV(this, 640, 480);

  redImg = createImage(width, height, RGB);
  greenImg = createImage(width, height, RGB);
  blueImg = createImage(width, height, RGB);

  loadPixels();
}

void draw() {
  if (video.available()) {
    video.read();
  }

  // OpenCVでカラー画像として読み込む
  opencv.loadImage(video);
  opencv.useColor(RGB);

  // OpenCVで赤・緑・青のチャンネルを取り出す
  opencv.toPImage(opencv.getR(), redImg);
  opencv.toPImage(opencv.getG(), greenImg);
  opencv.toPImage(opencv.getB(), blueImg);

  redImg.loadPixels();
  greenImg.loadPixels();
  blueImg.loadPixels();
  loadPixels();

  int shift = 12;  // 色をずらす量

  for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
      int index = y * width + x;

      // 赤は少し左、緑は中央、青は少し右から取り出す
      int rx = constrain(x - shift, 0, width - 1);
      int gx = x;
      int bx = constrain(x + shift, 0, width - 1);

      int rIndex = y * width + rx;
      int gIndex = y * width + gx;
      int bIndex = y * width + bx;

      float r = brightness(redImg.pixels[rIndex]);
      float g = brightness(greenImg.pixels[gIndex]);
      float b = brightness(blueImg.pixels[bIndex]);

      pixels[index] = color(r, g, b);
    }
  }

  updatePixels();
}
ずれ
実行結果:RGBチャンネルをずらして、色収差・グリッチ風に表示する

shiftの値を大きくすると、赤・緑・青のずれが大きくなります。色を分解して別々に扱うだけでも、映像の印象を大きく変えることができます。

まとめ

処理意味
カメラ映像の表示カメラからの映像を画面に表示する
明るさ画像全体を明るく/暗くする
コントラスト明暗の差を強く/弱くする
エッジ検出輪郭や境界線を取り出す
二値化白と黒の2色に分ける
ぼかし周囲の色を混ぜて画像をなめらかにする
顔検出画像の中から顔らしい領域を探す
動きの検出前フレームとの差を使って、動いている部分を取り出す
RGBチャンネルずらし赤・緑・青の成分を別々の位置から取り出して、グリッチ風に表示する

今日の重要ポイント:OpenCV for Processingを使うと、複雑な画像処理を短いコードで実行できます。まずは、カメラ映像を読み込み、明るさ、エッジ、二値化、ぼかしのような基本処理を試してみましょう。