このページでは、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を使う前に、カメラから映像を取得できるか確認します。
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. 明るさとコントラスト
明るさは画面全体を明るくしたり暗くしたりする処理です。コントラストは、明るい部分と暗い部分の差を強くしたり弱くしたりする処理です。
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. エッジの検出
エッジ検出は、画像の中で色や明るさが大きく変化している場所を取り出す処理です。輪郭線のような画像を作るときに使います。
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色に分ける処理です。明るさがしきい値より大きければ白、小さければ黒にします。
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();
}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);
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には顔以外にも、目、鼻、口などを検出するためのデータがあります。
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();
}8. 動きの検出
前のフレームと現在のフレームを比べると、動いている部分を取り出すことができます。これは「フレーム差分」と呼ばれる考え方です。ここではOpenCVのdiff()とthreshold()を使います。背景があまり動かない状態で、人や手だけが動くと、その部分だけが白く表示されます。
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()で色のチャンネルを取り出します。画像処理の基本である「色の成分を分けて扱う」ことが、視覚的にわかりやすいサンプルです。
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();
}shiftの値を大きくすると、赤・緑・青のずれが大きくなります。色を分解して別々に扱うだけでも、映像の印象を大きく変えることができます。
まとめ
| 処理 | 意味 |
|---|---|
| カメラ映像の表示 | カメラからの映像を画面に表示する |
| 明るさ | 画像全体を明るく/暗くする |
| コントラスト | 明暗の差を強く/弱くする |
| エッジ検出 | 輪郭や境界線を取り出す |
| 二値化 | 白と黒の2色に分ける |
| ぼかし | 周囲の色を混ぜて画像をなめらかにする |
| 顔検出 | 画像の中から顔らしい領域を探す |
| 動きの検出 | 前フレームとの差を使って、動いている部分を取り出す |
| RGBチャンネルずらし | 赤・緑・青の成分を別々の位置から取り出して、グリッチ風に表示する |
今日の重要ポイント:OpenCV for Processingを使うと、複雑な画像処理を短いコードで実行できます。まずは、カメラ映像を読み込み、明るさ、エッジ、二値化、ぼかしのような基本処理を試してみましょう。
