このページでは、Processingでカメラ映像を使った画像処理を学びます。画像処理の基本は、映像を読み込み、ピクセルの色を取り出し、別の色や図形に変換して描き直すことです。
Processingのサンプルコードは、映像が歪まないように基本的に 640×480 で記述しています。ブラウザ上のp5.js実行サンプルは、ページ上で扱いやすいように 320×240 で表示します。
1. カメラ映像の表示
まずは、カメラからの映像をProcessing上で表示します。Processingではprocessing.videoライブラリのCaptureを使います。
import processing.video.*;
Capture video;
void setup() {
size(640, 480);
String[] cameras = Capture.list();
// カメラが認識されるまで待つ
while (cameras.length == 0) {
cameras = Capture.list();
}
// 640×480でカメラ映像を取得する
video = new Capture(this, 640, 480, cameras[0]);
video.start();
}
void draw() {
if (video.available()) {
video.read();
}
image(video, 0, 0, width, height);
}2. tint()を使った効果
tint()を使うと、画像や映像に色を重ねることができます。ここでは、カメラ映像に赤っぽい色を重ねます。
import processing.video.*;
Capture video;
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();
}
void draw() {
if (video.available()) {
video.read();
}
// 映像に赤っぽい色を重ねる
tint(255, 100, 100);
image(video, 0, 0, width, height);
}3. 座標変換を使った反転
scale()を使うと、カメラ映像を左右反転・上下反転できます。左右反転ではscale(-1, 1)を使います。
import processing.video.*;
Capture video;
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();
}
void draw() {
if (video.available()) {
video.read();
}
background(0);
image(video, 0, 0, width/2, height/2); // 左上:正像
pushMatrix();
scale(-1, 1); // 右上:左右反転
image(video, -width, 0, width/2, height/2);
popMatrix();
pushMatrix();
scale(1, -1); // 左下:上下反転
image(video, 0, -height, width/2, height/2);
popMatrix();
pushMatrix();
scale(-1, -1); // 右下:上下左右反転
image(video, -width, -height, width/2, height/2);
popMatrix();
}4. 回転するカメラ映像
translate()とrotate()を使うと、カメラ映像を回転させることができます。画像の基準点を画面中央に移動してから回転させます。
import processing.video.*;
Capture video;
float angle = 0;
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();
imageMode(CENTER);
}
void draw() {
if (video.available()) {
video.read();
}
background(0);
translate(width/2, height/2);
rotate(angle);
image(video, 0, 0, 320, 240);
angle += 0.02;
}5. 1ピクセルずつ色を取り出す
画像処理では、1ピクセルずつ色を調べることが重要です。pixels[y * width + x]で、x,y座標のピクセルを配列番号として指定します。
import processing.video.*;
Capture video;
void setup() {
size(640, 480);
String[] cameras = Capture.list();
while (cameras.length == 0) {
cameras = Capture.list();
}
video = new Capture(this, width, height, cameras[0]);
video.start();
loadPixels();
}
void draw() {
if (video.available()) {
video.read();
}
video.loadPixels();
loadPixels();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int index = y * width + x;
pixels[index] = video.pixels[index];
}
}
updatePixels();
}6. 色を変換する
ピクセルの赤・緑・青の値を取り出すと、グレー変換、色の反転、2色化などができます。
import processing.video.*;
Capture video;
void setup() {
size(640, 480);
String[] cameras = Capture.list();
while (cameras.length == 0) {
cameras = Capture.list();
}
video = new Capture(this, width, height, cameras[0]);
video.start();
loadPixels();
}
void draw() {
if (video.available()) {
video.read();
}
video.loadPixels();
loadPixels();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int index = y * width + x;
color c = video.pixels[index];
float r = red(c);
float g = green(c);
float b = blue(c);
// グレー変換:RGBの平均を明るさにする
float gray = (r + g + b) / 3;
pixels[index] = color(gray);
// 反転:色を逆にする場合はこちらを使う
// pixels[index] = color(255 - r, 255 - g, 255 - b);
// 二値化:明るさで白と黒に分ける場合はこちらを使う
// if (gray > 127) {
// pixels[index] = color(255);
// } else {
// pixels[index] = color(0);
// }
}
}
updatePixels();
}7. 時間の遅延を記録する
1行ずつ、または1列ずつ映像を記録していくと、時間のずれが画面に残ります。動いているものが歪んで表示されるのは、行や列ごとに撮影された時間が違うからです。
import processing.video.*;
Capture video;
int row = 0;
void setup() {
size(640, 480);
String[] cameras = Capture.list();
while (cameras.length == 0) {
cameras = Capture.list();
}
video = new Capture(this, width, height, cameras[0]);
video.start();
loadPixels();
}
void draw() {
if (video.available()) {
video.read();
}
video.loadPixels();
// 1行分だけ現在の映像からコピーする
for (int x = 0; x < width; x++) {
int index = row * width + x;
pixels[index] = video.pixels[index];
}
updatePixels();
stroke(255, 0, 0);
line(0, row, width, row);
row++;
if (row >= height) {
row = 0;
}
}import processing.video.*;
Capture video;
int column = 0;
void setup() {
size(640, 480);
String[] cameras = Capture.list();
while (cameras.length == 0) {
cameras = Capture.list();
}
video = new Capture(this, width, height, cameras[0]);
video.start();
loadPixels();
}
void draw() {
if (video.available()) {
video.read();
}
video.loadPixels();
// 1列分だけ現在の映像からコピーする
for (int y = 0; y < height; y++) {
int index = y * width + column;
pixels[index] = video.pixels[index];
}
updatePixels();
stroke(255, 0, 0);
line(column, 0, column, height);
column++;
if (column >= width) {
column = 0;
}
}8. カメラ映像を図形で描く
カメラ映像をそのまま表示するのではなく、ピクセルの明るさや色を使って図形を描くと、モザイクや抽象的な映像表現になります。ここでは、低解像度のカメラ映像を読み取り、1つのピクセルを1つの図形として描き直します。
import processing.video.*;
Capture video;
void setup() {
size(640, 480);
String[] cameras = Capture.list();
while (cameras.length == 0) {
cameras = Capture.list();
}
video = new Capture(this, 80, 60, cameras[0]);
video.start();
noStroke();
}
void draw() {
if (video.available()) {
video.read();
}
video.loadPixels();
background(0);
float cellW = width / float(video.width);
float cellH = height / float(video.height);
for (int y = 0; y < video.height; y++) {
for (int x = 0; x < video.width; x++) {
int index = y * video.width + x;
color c = video.pixels[index];
float r = red(c);
float g = green(c);
float b = blue(c);
float brightness = (r + g + b) / 3;
// 明るいほど大きな円になる
float s = map(brightness, 0, 255, 2, cellW * 2.5);
fill(c);
ellipse(x * cellW, y * cellH, s, s);
}
}
}元の映像を円の集合に変換するだけでも、カメラ映像がポップなドット表現になります。コードは単純ですが、色と大きさの変化が大きいため、見た目の変化がわかりやすいサンプルです。
9. 明るさで文字のような模様を作る
次は、明るさに応じて図形の大きさを変えながら、少しだけ回転させます。短いコードで、光るタイルやデジタルサイネージのような派手な表現になります。
import processing.video.*;
Capture video;
void setup() {
size(640, 480);
String[] cameras = Capture.list();
while (cameras.length == 0) {
cameras = Capture.list();
}
video = new Capture(this, 80, 60, cameras[0]);
video.start();
rectMode(CENTER);
noStroke();
}
void draw() {
if (video.available()) {
video.read();
}
video.loadPixels();
background(0);
float cellW = width / float(video.width);
float cellH = height / float(video.height);
for (int y = 0; y < video.height; y += 2) {
for (int x = 0; x < video.width; x += 2) {
int index = y * video.width + x;
color c = video.pixels[index];
float brightness = (red(c) + green(c) + blue(c)) / 3;
float s = map(brightness, 0, 255, 2, 24);
pushMatrix();
translate(x * cellW, y * cellH);
rotate(frameCount * 0.04);
fill(c);
rect(0, 0, s, s);
popMatrix();
}
}
}このサンプルでは、明るさを四角形のサイズに変換しています。さらにrotate(frameCount * 0.04)で全ての四角形を回転させることで、簡単なコードでも動きのある映像になります。
まとめ
| 命令・考え方 | 意味 |
|---|---|
Capture | カメラ映像を取得する |
image() | 画像や映像を画面に表示する |
tint() | 画像や映像に色を重ねる |
scale(-1, 1) | 左右反転する |
loadPixels() | ピクセル配列を操作できるようにする |
pixels[y * width + x] | x,y座標のピクセルを配列番号で指定する |
| グレー変換 | RGBの平均を明るさとして使う |
| 時間の遅延 | 行や列ごとに違う時間の映像を記録する |
| ドット表現 | 明るさを円の大きさに変換する |
| タイル表現 | 明るさを四角形の大きさや回転に変換する |
今日の重要ポイント:画像処理の基本は、映像をそのまま表示することではなく、ピクセルの色を取り出して、別の色や別の図形に変換して描き直すことです。
