このページでは、Processingでカメラ映像を使った画像処理を学びます。画像処理というと難しく聞こえますが、基本は「映像を読み込む」「色を変える」「1ピクセルずつ調べる」「別の図形で描き直す」という流れです。
p5.jsの実行欄では、ブラウザのカメラを使ってProcessingのサンプルに近い動きを再生します。最初にカメラ開始ボタンを押し、ブラウザのカメラ使用を許可してください。
1. カメラ映像の表示
まずは、カメラからの映像をProcessing上で表示します。Processingではprocessing.videoライブラリのCaptureを使います。
import processing.video.*;
Capture video;
void setup() {
size(400, 400);
String[] cameras = Capture.list();
// カメラが認識されるまで待つ
while (cameras.length == 0) {
cameras = Capture.list();
}
// 最初に見つかったカメラを使う
video = new Capture(this, 400, 400, cameras[0]);
video.start();
}
void draw() {
// 新しい映像が来ていたら読み込む
if (video.available()) {
video.read();
}
image(video, 0, 0, width, height);
}video.read()でカメラの映像を読み込み、image(video, 0, 0)で画面に表示しています。
2. tint()を使った効果
tint()を使うと、画像や映像に色を重ねることができます。tint(r, g, b, alpha)のように書くと、不透明度も指定できます。
import processing.video.*;
Capture video;
void setup() {
size(400, 400);
String[] cameras = Capture.list();
while (cameras.length == 0) {
cameras = Capture.list();
}
video = new Capture(this, 400, 400, 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)を使います。ただしx方向が反転するので、表示位置を-widthにする必要があります。
import processing.video.*;
Capture video;
void setup() {
size(400, 400);
String[] cameras = Capture.list();
while (cameras.length == 0) {
cameras = Capture.list();
}
video = new Capture(this, 200, 200, cameras[0]);
video.start();
}
void draw() {
if (video.available()) {
video.read();
}
background(0);
image(video, 0, 0, 200, 200); // 左上:正像
pushMatrix();
scale(-1, 1); // 左右反転
image(video, -400, 0, 200, 200);
popMatrix();
pushMatrix();
scale(1, -1); // 上下反転
image(video, 0, -400, 200, 200);
popMatrix();
pushMatrix();
scale(-1, -1); // 上下左右反転
image(video, -400, -400, 200, 200);
popMatrix();
}4. 回転するカメラ映像
translate()とrotate()を使うと、カメラ映像を回転させることもできます。画像の基準点を画面中央に移動してから回転させるのがポイントです。
import processing.video.*;
Capture video;
float angle = 0;
void setup() {
size(400, 400);
String[] cameras = Capture.list();
while (cameras.length == 0) {
cameras = Capture.list();
}
video = new Capture(this, 240, 180, 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);
angle += 0.02;
}5. 1ピクセルずつ色を取り出す
画像処理では、1ピクセルずつ色を調べることが重要です。Processingでは、loadPixels()でピクセル配列を使えるようにし、pixels[y * width + x]で目的のピクセルを指定します。
import processing.video.*;
Capture video;
void setup() {
size(400, 400);
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++) {
// x,y座標を配列番号に変換する
int index = y * width + x;
pixels[index] = video.pixels[index];
}
}
updatePixels();
}6. 色を変換する
ピクセルの赤・緑・青の値を取り出すと、グレー変換、色の反転、2色化などができます。
| 処理 | 考え方 |
|---|---|
| グレー変換 | 赤・緑・青の平均を使う |
| 色の反転 | 255 - 色にする |
| 2色化 | 明るさがしきい値以上なら白、未満なら別の色にする |
import processing.video.*;
Capture video;
void setup() {
size(400, 400);
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);
}
}
updatePixels();
}7. 時間の遅延を記録する
1行ずつ、または1列ずつ映像を記録していくと、時間のずれが画面に残ります。動いているものが歪んで表示されるのは、行や列ごとに撮影された時間が違うからです。
import processing.video.*;
Capture video;
int row = 0;
void setup() {
size(400, 400);
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();
// 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(400, 400);
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();
// 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. カメラ映像を図形で描く
カメラ映像をそのまま表示するのではなく、ピクセルの明るさや色を使って図形を描くと、モザイクや線画のような表現ができます。
import processing.video.*;
Capture video;
void setup() {
size(400, 400);
String[] cameras = Capture.list();
while (cameras.length == 0) {
cameras = Capture.list();
}
video = new Capture(this, 80, 80, cameras[0]);
video.start();
rectMode(CENTER);
noFill();
}
void draw() {
if (video.available()) {
video.read();
}
video.loadPixels();
background(0);
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 brightness = (red(c) + green(c) + blue(c)) / 3;
float rectSize = brightness / 255 * 20;
stroke(c);
rect(x * 5, y * 5, rectSize, rectSize);
}
}
}import processing.video.*;
Capture video;
void setup() {
size(400, 400);
String[] cameras = Capture.list();
while (cameras.length == 0) {
cameras = Capture.list();
}
video = new Capture(this, 80, 80, cameras[0]);
video.start();
}
void draw() {
if (video.available()) {
video.read();
}
video.loadPixels();
background(0);
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 brightness = (red(c) + green(c) + blue(c)) / 3;
float lineSize = brightness / 255 * 30;
stroke(c);
line(x * 5 - lineSize/2, y * 5,
x * 5 + lineSize/2, y * 5);
}
}
}まとめ
| 命令・考え方 | 意味 |
|---|---|
Capture | カメラ映像を取得する |
image() | 画像や映像を画面に表示する |
tint() | 画像や映像に色を重ねる |
scale(-1, 1) | 左右反転する |
loadPixels() | ピクセル配列を操作できるようにする |
pixels[y * width + x] | x,y座標のピクセルを配列番号で指定する |
| グレー変換 | RGBの平均を明るさとして使う |
| 時間の遅延 | 行や列ごとに違う時間の映像を記録する |
今日の重要ポイント:画像処理の基本は、映像をそのまま表示することではなく、ピクセルの色を取り出して、別の色や別の図形に変換して描き直すことです。
