Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

メディアデザインセミナー１
視覚メディア研究室

1

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

内容◼ Visual Studio を用いた C++ プログラミング◼ OpenCV によるビデオ入力◼ OpenGL による図形表示

だだし直接 OpenGL のプログラミングは行いません

2

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

Visual Studio 2017 の起動
ソリューションファイルを開く

12

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

とりあえず信用してください

18

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

Visual Studio 2017 起動

19

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

プロジェクトのビルドと実行作成したプログラムを動かす

20

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

kadai1 の▶をクリック

21

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

ソースファイルの▶をクリック

kadai1.cpp をダブルクリック
このソースファイル kadai1.cpp を修正する

22

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

実行（デバッグ）

23

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

ビルドしていなければこのウィンドウが表示されるので
ビルドする

24

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

クローズボタンをクリックするか
Esc キーをタイプすれば終了する

ビルドに成功すれば実行される

25

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

停止（強制終了）ボタン

デバッグ中の Visual Studio 201726

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

ソリューション全体の保存

27

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

課題
ビデオにエフェクトをかけるプログラムを作る

44

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

課題テンプレートの実行結果

45

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

目標の実行結果（ビデオエフェクト）

46

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

1 日目
47

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

kadai1.cpp の内容// 共通の関数#include "common.h"◼

//// 設定（最初に一度だけ実行されます）//
bool setup()
ここにプログラムを書きます{◼

setup() 関数と update() 関数の中身を実装します

setup() 関数

プログラムの実行開始時に一度だけ実行されます
false を返すとメッセージを表示してプロ
グラムを停止します// セットアップに成功した
return true;}//// 更新（毎回実行されます）//
bool update()
ここにプログラムを書きます{

kadai1.cpp を修正してください◼

update() 関数

画面表示を行うたびに毎回実行されます
false を返すとプログラムを終了します// プログラムを終了しない
return true;}
48

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama Universitycommon.h の内容◼

// 点データの転送
extern void submitPoint(int width, int height,
const void *point);◼

createPoint()// 色データの作成
extern void createColor(int width, int height,
const void *color = nullptr);◼

submitPoint()◼

createColor()◼

submitColor()◼

getTime()// 色データの転送
extern void submitColor(int width, int height,
const void *color);// 現在時刻の取得
extern float getTime();49

common.h にはプログラムの作成に必要な関数を宣言しています// 点データの作成
extern void createPoint(int width, int height,
const void *point = nullptr);

もちろん他に C / C++ の関数も使えます
点のデータを保持するメモリを確保してそこにデータを転送します
点のデータを転送します

色のデータを保持するメモリを確保してそこにデータを転送します
色のデータを転送します
現在時刻を秒で取得します
April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

createPoint(int width, int height, const void *point)// 点群の幅
const int width(160);// 点群の高さ
const int height(120);◼

width に点群の幅を指定します
height に点群の高さを指定します
point は点のデータを格納した配列です

これに値を設定します// 点のデータ
float point[height][width][3];点のデータを保持するメモリを確保してそこにデータを転送します◼

::// 点のデータを保持するメモリを確保してそこにデータを転送する
createPoint(width, height, point);

point

一つの点は x, y, z の三つの値 (座標値) を持ちます
これが幅 width 個，高さ height 個並んでいます
値のデータ型は float ですpoint の指定を省略 (あるいは nullptr を指定) した場合はデータを転送しません◼ メモリの確保だけを行います◼
50

setup() で実行する必要があります
April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

点のデータ

height = 4点のデータは座標値

width = 5
51

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

submitPoint(int width, int height, const void *point)// 点群の幅
const int width(160);// 点群の高さ
const int height(120);◼

width に点群の幅を指定します
height に点群の高さを指定します
point は点のデータを格納した配列です

これに値を設定します// 点のデータ
float point[height][width][3];

◼

point

◼

update() で実行する必要があります::// 点のデータを転送する
submitPoint(width, height, point);

52

点のデータの転送のみを行います

一つの点は x, y, z の三つの値 (座標値) を持ちます
これが幅 width 個，高さ height 個並んでいます
値のデータ型は float ですpoint の指定は省略できません

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

createColor(int width, int height, const void *point)// 画像の幅
const int width(640);// 画像の高さ
const int height(480);◼

width に画像の幅を指定します
height に画像の高さを指定します
color は色のデータを格納した配列です

これに値を設定します// 色のデータ
unsigned char color[height][width][3];色のデータを保持するメモリを確保してそこにデータを転送します◼

::// 色のデータを保持するメモリを確保してそこにデータを転送する
createColor(width, height, color);color

一つの色は r, g, b の三つの値 (三原色) を持ちます
これが幅 width 個，高さ height 個並んでいます
値のデータ型は unsigned char ですcolor の指定を省略 (あるいは nullptr を指定) した場合はデータを転送しません◼ メモリの確保だけを行います◼
53

setup() で実行する必要があります
April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

色のデータ

height = 3

色のデータは三原色

width = 4
54

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

submitColor(int width, int height, const void *point)// 画像の幅
const int width(640);// 画像の高さ
const int height(480);◼

width に画像の幅を指定します
height に画像の高さを指定します
color は色のデータを格納した配列です

これに値を設定します// 色のデータ
unsigned char color[height][width][3];

◼

color

◼

update() で実行する必要があります::// 色のデータを転送する
submitColor(width, height, color);55

色のデータの転送のみを行います

一つの色は r, g, b の三つの値 (三原色) を持ちます
これが幅 width 個，高さ height 個並んでいます
値のデータ型は unsigned char ですcolor の指定は省略できません

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

width, height◼ 点群の幅 (width) と高さ (height) と画像の幅 (width) と高さ(height) は一致している必要はありません◼ submitPoint(width, height, point) で指定する width, height には createPoint(width, height, point) で指定した width, height
より大きな値を指定することはできません◼ submitColor(width, height, color) で指定する width, height には createColor(width, height, color) で指定した width, height
より大きな値を指定することはできません

56

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

点を格子状に並べる// メッシュの幅と高さ
const int width(5), height(4);

パラメータ
𝑢, 𝑣

// 点データ
float point[height][width][3];for (int j = 0; j < height; ++j)𝑥, 𝑦, 𝑧{点の座標値
for (int i = 0; i < width; ++i){// x 方向のパラメータ (0≦u≦1)
const float u(float(i) / float(width - 1));// y 方向のパラメータ (0≦v≦1)
const float v(float(j) / float(height - 1));// 頂点の位置
point[j][i][0] = u * 2.0f - 1.0f;
point[j][i][1] = v * 2.0f - 1.0f;
point[j][i][2] = 0.0f;

}

57

}

(省略)

𝑥 = 2𝑢 − 1
ቐ𝑦 = 2𝑣 − 1
𝑧=0
April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

実行結果◼

kadai1.cpp の設定
width = 100
height = 100

58

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

点を円柱状に並べる// 円周率const float pi(3.14159265f);

直径２for (int j = 0; j < height; ++j){for (int i = 0; i < width; ++i){// x 方向のパラメータ (0≦u≦1)
const float u(float(i) / float(width - 1));// 経度
const float longtude(2.0f * pi * u);𝑢, 𝑣// y 方向のパラメータ (0≦v≦1)
const float v(float(j) / float(height - 1));// 頂点の位置
point[j][i][0] = sin(longtude);point[j][i][1] = v * 2.0f - 1.0f;
point[j][i][2] = cos(longtude);

}

59

}

(省略)

𝑥, 𝑦, 𝑧

高さ２𝑥 = sin 2𝜋𝑢൞ 𝑦 = 2𝑣 − 1
𝑧 = cos 2𝜋𝑢April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

実行結果

60

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

場所によって色を変える
for (int j = 0; j < height; ++j){for (int i = 0; i < width; ++i){// x 方向のパラメータ (0≦u≦1)
const float u(float(i) / float(width - 1));sin 𝜋𝑣// 経度
const float longtude(2.0f * pi * u);// y 方向のパラメータ (0≦v≦1)
const float v(float(j) / float(height - 1));

sin 10𝜋𝑣// 緯度
const float latitude(pi * v);// 緯度に対応した色のスケール
const float latitude_color(sin(10.0f * pi * v) * 0.5f + 0.5f);
(省略)

}
61

}

// 頂点の色
color[j][i][0] = 100;color[j][i][1] = unsigned char(255 * latitude_color);
color[j][i][2] = 0;

色のデータの最大値

11
sin 10𝜋𝑣 +22
0 ≤ 𝑙𝑎𝑡𝑖𝑡𝑢𝑑𝑒_𝑐𝑜𝑙𝑜𝑟 ≤ 1

unsigned char 型には 0～255 の整数値を格納できる
April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

実行結果

62

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

直径２

課題1
◼

球を描いてください

円柱をベースにして結構です
その場合北極と南極は複数の点が重なっていても構いません

𝑢, 𝑣
𝑥 = sin 𝜋𝑣 sin 2𝜋𝑢
𝑦 = − cos 𝜋𝑣൞𝑧 = sin 𝜋𝑣 cos 2𝜋𝑢

63

高さ２

𝑥, 𝑦, 𝑧April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

課題1 実行結果◼

◼

64

スクリーンショットを 01.png というファイル名でアップロードしてください
すでに 01.png がある場合は上書
きしてくださいApril 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

課題2◼

縦縞をつけてくださいあるいは好きな模様をつけてくだ
さい

◼

65

スクリーンショットを 02.png というファイル名でアップロードしてください

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

時間に従って変形する◼ update() で point に値を設定する

update() は繰り返し実行される◼ point に設定する値に時間の関数を用いる

時間は getTime() 関数で得られる◼ たとえば float scale = sin(2.0f * pi * getTime()) * 0.2f + 1.0f; とすれば scale は

1 秒ごとに 0.8 ～ 1.2 の間を変化する値をとる◼ この scale を point に代入する座標値に乗じれば図形は拡大縮小を繰り返す◼ 求めた point の値は submitPoint() を使って転送する

submitPoint(width, height, point);

66

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

kadai1.cpp の変更点 (1/5)// 周期
const float cycle(1.0f);//// 設定（最初に一度だけ実行されます）//
bool setup(){for (int j = 0; j < height; ++j){for (int i = 0; i < width; ++i){// x 方向のパラメータ (0≦u≦1)
const float u(float(i) / float(width - 1));

追加

update() にコピー// 経度
const float longtude(2.0f * pi * u);// 経度に対応した色のスケール
const float longtude_color(sin(10.0f * pi * u) * 0.5f + 0.5f);
67

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

kadai1.cpp の変更点 (2/5)// y 方向のパラメータ (0≦v≦1)
const float v(float(j) / float(height - 1));// 緯度
const float latitude(pi * v);

update() にコピー// 緯度に対応した色のスケール
const float latitude_color(sin(10.0f * pi * v) * 0.5f + 0.5f);// 頂点の位置
point[j][i][0] = sin(latitude) * sin(longtude);point[j][i][1] = -cos(latitude);point[j][i][2] = sin(latitude) * cos(longtude);update() に移動// 頂点の色
color[j][i][0] = 100;color[j][i][1] = unsigned char(255 * latitude_color);
color[j][i][2] = unsigned char(255 * longtude_color);}}
68

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

kadai1.cpp の変更点 (3/5)// 点データを登録する
createPoint(width, height);

point は省略// 色データを登録する
createColor(width, height, color);// セットアップに成功した
return true;}//// 更新（毎回実行されます）//
bool update(){// 時間に従って位置を変化させる
const float offset(sin(2.0f * pi * fmod(getTime(), cycle) / cycle));for (int j = 0; j < height; ++j){for (int i = 0; i < width; ++i)
69

追加
setup() からコピー
April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

kadai1.cpp の変更点 (4/5){// x 方向のパラメータ (0≦u≦1)
const float u(float(i) / float(width - 1));// 経度
const float longtude(2.0f * pi * u);// 経度に対応した色のスケール
const float longtude_color(sin(10.0f * pi * u) * 0.5f + 0.5f);

setup() からコピー// y 方向のパラメータ (0≦v≦1)
const float v(float(j) / float(height - 1));// 緯度
const float latitude(pi * v);// 緯度に対応した色のスケール
const float latitude_color(sin(10.0f * pi * v) * 0.5f + 0.5f);// 形状のスケール
const float scale(offset + 1.0f);
70

追加
April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

kadai1.cpp の変更点 (5/5)// 頂点の位置
point[j][i][0] = scale * ( ... );point[j][i][1] = scale * ( ... );point[j][i][2] = scale * ( ... );update() の中で
point に代入}

setup() からコピー}// 点データを登録する
submitPoint(width, height, point);

setup() から移動して各要素を scale 倍point を転送する

追加// プログラムを終了しない
return true;}

71

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

fmod() 関数◼ 実数の剰余を求める

fmod(x, y) → 𝑥 − 𝑥 Τ𝑦 𝑦
◼ fmod(15.6, 2.3) → 15.6 −15.6Τ2.3 × 2.3 = 15.6 − 6 × 2.3 = 1.8

z

z
𝑧=𝑥

𝑧 = fmod 𝑥, 2.3

2.3

x
72

x
April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

実行結果

73

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

課題3
◼
◼

形の変化の仕方が場所によって異なるようにしてくださいスクリーンショットを 03.png というファイル名でアップロードし
てください

作例
74

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

課題3のそのほかの作例
一枚板を変形した場合

75

円柱を変形した場合

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

2 日目
76

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

長方形を描く//// 更新（毎回実行されます）//
bool update(){for (int j = 0; j < height; ++j){for (int i = 0; i < width; ++i){// x 方向のパラメータ (0≦u≦1)
const float u(float(i) / float(width - 1));// y 方向のパラメータ (0≦v≦1)
const float v(float(j) / float(height - 1));}}// 頂点の位置
point[j][i][0] = 2.0f * u - 1.0f;
point[j][i][1] = 4.0f * v - 2.0f;
point[j][i][2] = 0.0f;// 点データを登録する
submitPoint(width, height, point);}77// プログラムを終了しない
return true;

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

波型にする
//
// 更新（毎回実行されます）//
bool update(){for (int j = 0; j < height; ++j){for (int i = 0; i < width; ++i){// x 方向のパラメータ (0≦u≦1)
const float u(float(i) / float(width - 1));// y 方向のパラメータ (0≦v≦1)
const float v(float(j) / float(height - 1));}}// 頂点の位置
point[j][i][0] = 2.0f * u - 1.0f;
point[j][i][1] = 4.0f * v - 2.0f;
point[j][i][2] = sin(8.0f * pi * v) * 0.2f;// 点データを登録する
submitPoint(width, height, point);}78// プログラムを終了しない
return true;

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

波を動かす
//
// 更新（毎回実行されます）//
bool update(){// 時間に従って位置を変化させる
const float offset(2.0f * pi * fmod(getTime(), cycle) / cycle);for (int j = 0; j < height; ++j){for (int i = 0; i < width; ++i){// x 方向のパラメータ (0≦u≦1)
const float u(float(i) / float(width - 1));// y 方向のパラメータ (0≦v≦1)
const float v(float(j) / float(height - 1));}}// 頂点の位置
point[j][i][0] = 2.0f * u - 1.0f;
point[j][i][1] = 4.0f * v - 2.0f;
point[j][i][2] = sin(8.0f * pi * v + offset) * 0.2f;// 点データを登録する
submitPoint(width, height, point);}

79

位相を変化させる// プログラムを終了しない
return true;

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

課題4◼

波が板の中心から発生するようにしてくださいもしギザギザが目立つと感じるな
ら width と height を少し大きくしてみてください◼

スクリーンショットを 04.png というファイル名でアップロードし
てください

作例
80

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

OpenCV の画像データ◼ cv::Mat クラス（行列クラス）

コンストラクタ◼ cv::Mat(), cv::Mat(int _rows, int _cols, int _type), ほか_rows: 画像の高さ，_cols: 画像の幅
_type はデータが符号なし (unsigned) 8 ビット 3 チャンネル (RGB) 画像の時 CV_8UC3

メンバ◼ rows: 高さ，cols: 幅，data: データ本体（入っていなければ nullptr），ほか

メソッド◼ type(): _type を返す, depth(): ビット深度 (CV_8U とか) を返す，

channels(): チャンネル数を返す◼ isContinuous(): これが false だと data にデータが連続して入っていない
その場合 data メンバをそのまま使えない81

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

画像の読み込み◼ cv::imread(const std::string &filename, int flags = 1)

引数

◼ filename: ファイル名の文字列（std::string クラス）
◼ flags : （省略した時は 1）

flags > 0 ⇒ 強制的に 3 チャンネルカラー画像として読み込む
flags = 0 ⇒ 強制的に 1 チャンネルグレースケール画像として読み込む
flags < 0 ⇒そのままの画像として読み込まれる

戻り値◼ cv::Mat 型の画像データ読み込めなかったときは data が nullptr（empty() メソッドが true）

82

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

画像を読み込む// 共通の関数#include "common.h"◼

// OpenCV#include <opencv2/opencv.hpp>

cmath より先に #include しないとエラーになるかもしれません// 標準ライブラリ#include <cmath>（省略）//// 設定（最初に一度だけ実行されます）//
bool setup(){（省略）// 画像の読み込み
const cv::Mat image(cv::imread("image.jpg"));// 色データを登録する
createColor(image.cols, image.rows, image.data);

83

プログラムの冒頭で OpenCV のヘッダファイル opencv2/opencv.hpp を#include します◼

imread() で image.jpg ファイルを読み込んで image に格納します

image.jpg は kadai1 フォルダに入っています◼

これは手を抜いています

image.data が nullptr でないことを確かめるべきです
image.isContinuous() が false でないことを確かめるべきです
April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

画像の読み込みに失敗した時の処理の追加//// 設定（最初に一度だけ実行されます）//
bool setup(){（省略）// 画像の読み込み
const cv::Mat image(cv::imread("image.jpg"));// 画像の読み込みに失敗した
if (image.empty()) return false;

画像が読み込めなかったときは data
メンバが nullptr になっています◼ そのとき empty() メソッドは true を返します◼

setup() が false で return すると課題プロ
グラムはメッセージを出して終了するよ
うに作ってあります
ここに自分でエラー処理を追加できます// 色データを登録する
createColor(image.cols, image.rows, image.data);

84

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

課題5◼

模様を付ける代わりに画像を貼り付けてください画像 (image.jpg) は自分で撮影したものなど他のものに入れ替えてください
大学のコンピュータへの保存方法
は教員に相談してください◼

スクリーンショットを 05.png というファイル名でアップロードし
てください

作例
85

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

動画の読み込み◼ cv::VideoCapture クラス

コンストラクタ◼ cv::VideoCapture(), cv::VideoCapture(const std::string &filename),

cv::VideoCapture(int device)filename: ムービーファイル名， device: キャプチャデバイス (Web カメラ等) の番号

メソッド◼ open(const std::string &filename): ムービーファイルを開く, open(int device):

キャプチャデバイスを開く, grab(): フレームを取得する（取得できなければfalse を返す）, retrieve(cv::Mat &image, int channel = 0): 取得したフレーム
を image に格納する, set(int propId, double value): プロパティpropId を設定
する, get(int propId): プロパティpropId を取得する

演算子◼ >>: フレームを取得 (grab()) して格納 (retrieve()) する

86

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

動画を開く// ビデオ入力
cv::VideoCapture video;//// 設定（最初に一度だけ実行されます）//
bool setup(){（省略）// ビデオの取得開始
video.open("video.mp4");// ムービーファイルの1フレームの一時保存先
cv::Mat frame;// 最初のフレームを取得する
video >> frame;// 色データを登録する
createColor(frame.cols, frame.rows, frame.data);}

87// セットアップに成功した
return true;

動画の場合はフレーム（1枚の画像）を繰り返し読むことになります◼ cv::VideoCapture クラスのオブジェク
ト video を外部変数として宣言してsetup() と update() で共有します◼ setup() で open() メソッドを使ってムービーファイル video.mp4 を開きます◼

video.mp4 は kadai1 フォルダに入っていますムービーファイルのフレームを保存する cv::Mat 型の変数 frame を用意して最初のフレームを取得します◼ 取得したフレームを色データとして転送します◼

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

フレームを読み込む//// 更新（毎回実行されます）//
bool update(){（省略）// ムービーファイルの1フレームの一時保存先
cv::Mat frame;// 1フレーム取得する
video >> frame;// 取得したフレームを転送する
submitColor(frame.cols, frame.rows, frame.data);// プログラムを終了しない
return true;

ムービーファイルの１フレームを保存する cv::Mat 型の変数 frame を用意します◼ video から１フレーム取得して frameに保存します◼ 保存したフレームを転送します◼ これは手を抜いています◼

setup() でムービーファイルを正しく開く
ことができたか確かめるべきですupdate() でムービーファイルの最後に達した時の処理を追加すべきです}88

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

動画が開けなかったときの処理の追加//// 設定（最初に一度だけ実行されます）//
bool setup()
{
（省略）

◼

ムービーファイルやデバイスが開けたかどうかは isOpened() メソッドでも調べることができます// ビデオの取得開始
if (!video.open("video.mp4")) return false;// ムービーファイルの1フレームの一時保存先
cv::Mat frame;// 最初のフレームを取得する
if (!video.grab()) return false;
video.retrieve(frame);// 色データを登録する
createColor(frame.cols, frame.rows, frame.data);// セットアップに成功した
return true;

ムービーファイルやデバイスが開けないときは open() メソッドの戻り値
が false になります◼

>> 演算子の代わりに grab() メソッドと retrieve() メソッドを使ってフレームを取得します
フレームが取得できなければ grab() メ
ソッドの戻り値が false になります>> 演算子は grab() メソッドと retrieve()
メソッドを組み合わせた処理をします}89

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

フレームが取得できなかったときの処理の追加//// 更新（毎回実行されます）//
bool update(){（省略）

grab() メソッドを使って video から
１フレーム取り出します◼ grab() メソッドが false を返した場合は終了します◼

update() が false で return すると課題プ
ログラムは何も言わずに終了するように作ってあります// ムービーファイルの1フレームの一時保存先
cv::Mat frame;// 1フレーム取得する
if (!video.grab()) return false;
video.retrieve(frame);// 取得したフレームを転送する
submitColor(frame.cols, frame.rows, frame.data);◼

取り出したフレームは retrieve() メ
ソッドを使って保存します// プログラムを終了しない
return true;}

90

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

繰り返し再生する// 1フレーム取得する
if (!video.grab()){// フレームが取得できなかったらムービーファイルを巻き戻す
video.set(CV_CAP_PROP_POS_MSEC, 0.0);}
else{// ムービーファイルの1フレームの一時保存先
cv::Mat frame;// 取得したフレームを保存する
video.retrieve(frame);

これまでのプログラムはムービー
ファイルの最後に達すると何も言わずに終了します◼ そこでムービーファイルの最後に達した時は巻き戻すようにします◼

//// 更新（毎回実行されます）//
bool update(){（省略）

video.grab()の後に移動// 取得したフレームを転送する
submitColor(frame.cols, frame.rows, frame.data);

ムービーファイルの再生位置は
CV_CAP_PROP_POS_MSEC プロパティ，
CV_CAP_PROP_POS_FRAMES プロパティ，
あるいは CV_CAP_PROP_POS_AVI_RATIO
プロパティに設定します
CV_CAP_PROP_POS_MSEC◼ ミリ秒単位

CV_CAP_PROP_POS_FRAMES◼ フレーム単位

CV_CAP_PROP_POS_AVI_RATIO◼ 最初を 0，最後を 1 とした 0～1 の割合}
91April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

最初のフレームの取得時刻を記録する// ビデオ入力
cv::VideoCapture video;// 最初のフレームを取得した時刻
double start;//// 設定（最初に一度だけ実行されます）//
bool setup(){（省略）// ムービーファイルのフレームの一時保存先
cv::Mat frame;// 最初のフレームを取得する
if (!video.grab()) return false;// 最初のフレームを取得した時刻を記録しておく
start = getTime();// 取得したフレームを保存する
video.retrieve(frame);
92

演習室の PC の画面表示は通常 60Hz
で行われているのに対してムービー
ファイルは 30fps になっています◼ そのため現状では倍速で表示されてしまいます◼ そこで画面表示の時刻がムービー
ファイルの次のフレームの時刻に達していなかったら画像を更新しない
ようにします◼

画面表示の時刻は update() が呼び出され
た時刻です

◼

まず setup() で最初のフレームを取得
した時刻を記録しておきます
April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

ムービーを画面表示のタイミングに合わせる//// 更新（毎回実行されます）//
bool update(){// 経過時間
const double elaps(getTime() – start);// 経過時間が現在のフレームの時刻に達していたら
if (elaps >= video.get(CV_CAP_PROP_POS_MSEC) * 0.001){// 1フレーム取得する
if (!video.grab()){// フレームが取得できなかったらムービーファイルを巻き戻す
video.set(CV_CAP_PROP_POS_FRAMES, 0.0);// 経過時間をリセットする
start = getTime();

}

}
93

}
else
{
（省略）
}

現在の時刻から最初のフレームの時
刻を引いて経過時間 elaps を求めます◼ その経過時間 elaps がこれから取得しようとするフレームの時刻に達していたらフレームを取得します◼

フレームの時刻は
CV_CAP_PROP_POS_MSEC プロパティで取得できますただしミリ秒単位なので秒単位に直すた
めに 1/1000 (0.001) 倍します// プログラムを終了しない
return true;

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

課題6◼

画像を貼り付けるかわりにムー
ビーファイルを貼り付けてくださいムービーファイル (video.mp4) はk可能なら自分で撮影したものなど他のものに入れ替えてください
大学のコンピュータへの保存方法
は教員に相談してください◼

スクリーンショットを 06.png というファイル名でアップロードし
てください

作例
94

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

Web カメラから入力する// ビデオ入力
cv::VideoCapture video;//// 設定（最初に一度だけ実行されます）//
bool setup(){（省略）// ビデオの取得開始
video.open(0);デバイス番号// ムービーファイルの1フレームの一時保存先
cv::Mat frame;// 最初のフレームを取得する
video >> frame;

ムービーファイルの代わりに Web カ
メラから入力することもできます◼ open() メソッドの引数にデバイス番
号（PC に Web かめらが一つしかな
ければ多分 0）を指定します◼ cv::VideoCapture クラスはネットワー
クカメラにも対応しています◼

open() メソッドの引数に URL を指定できます（http://user:password@camera）// 色データを登録する
createColor(frame.cols, frame.rows, frame.data);}

95// セットアップに成功した
return true;

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

グレースケール化//// 更新（毎回実行されます）//
bool update(){（省略）// 経過時間がフレームの時刻に達していたら
if (elaps >= video.get(CV_CAP_PROP_POS_MSEC) * 0.001){// 1フレーム取得する
if (!video.grab())
{
（省略）
}
else
{
// ムービーファイルのフレームの一時保存先
cv::Mat frame;// 取得したフレームを保存する
video.retrieve(frame);// グレースケール化
cv::Mat gray;cv::cvtColor(frame, gray, CV_BGR2GRAY);
cv::cvtColor(gray, frame, CV_GRAY2BGR);

}

}

96

}

◼

cv::cvtColor() を使えば画像の色空間を変換できますCV_BGR2GRAY を指定すればカラー画像
をグレースケール画像（モノクロ画像）に変換しますただしグレースケール画像は1チャンネルしかないので submitColor() の色データには使えません
そこでもう一度 cv::cvtColor() を使ってグレースケール画像をカラー画像に変換します (CV_GRAY2BGR)// 保存したフレームを転送する
submitColor(frame.cols, frame.rows, frame.data);// プログラムを終了しない
return true;

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

実行結果

97

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

エッジ検出
（省略）
// ムービーファイルのフレームの一時保存先
cv::Mat frame;// 取得したフレームを保存する
video.retrieve(frame);// グレースケール化
cv::Mat gray;cv::cvtColor(frame, gray, CV_BGR2GRAY);// エッジ検出
cv::Mat sobelX, sobelY;
cv::Sobel(gray, sobelX, CV_8U, 1, 0);
cv::Sobel(gray, sobelY, CV_8U, 0, 1);gray = (sobelX + sobelY) * 0.5;
cv::cvtColor(gray, frame, CV_GRAY2BGR);

エッジ検出にはいくつもの手法がありますが，ここでは Sobel オペレー
タを使ってみます◼ Sobel オペレータは画像上のある方向に対する一次微分で，ここでは X 方
向と Y 方向について微分した結果の平均を求めています◼

// 保存したフレームを転送する
submitColor(frame.cols, frame.rows, frame.data);（省略）
98

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

実行結果

99

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

顔検出
（省略）
// カスケード分類器
cv::CascadeClassifier ccls;//// 設定（最初に一度だけ実行されます）//
bool setup(){（省略）// 色データを登録する
createColor(frame.cols, frame.rows, frame.data);// 顔の分類器を読み込む
ccls.load("../opencv/etc/haarcascades/haarcascade_frontalface_default.xml");// セットアップに成功した
return true;//// 更新（毎回実行されます）//
bool update(){（省略）// 経過時間がフレームの時刻に達していたら
if (elaps >= video.get(CV_CAP_PROP_POS_MSEC) * 0.001){// 1フレーム取得する
if (!video.grab())
{
（省略）
}
else
{
// ムービーファイルのフレームの一時保存先
cv::Mat frame;// 取得したフレームを保存する
video.retrieve(frame);}// グレースケール化
cv::Mat gray;cv::cvtColor(frame, gray, CV_BGR2GRAY);

このかわりに “../opencv/etc/haarcascades/haarcascade_eye.xml” を使えば「目」の検出ができる// 物体検出
std::vector<cv::Rect> objects;
ccls.detectMultiScale(gray, objects);for (auto &o : objects) cv::rectangle(frame, o, cv::Scalar(0, 0, 255));// 保存したフレームを転送する
submitColor(frame.cols, frame.rows, frame.data);}}
100

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

実行結果◼

検出した顔に枠を付けます

Web カメラを使わないと面白くないです◼

図形の原点が左下（上が y の正方向）なのに対して画像の原点は左上（下が y の正方向）なので上下が反転しています
そこは自分で直してください

101

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

明るさを高さで表す（左から続く）//// 更新（毎回実行されます）//
bool update(){（削除）// 点データの大きさに拡大縮小
cv::resize(gray, gray, cv::Size(width, height));// グレースケールを点の座標値の Z 値に使う
for (int j = 0; j < height; ++j){for (int i = 0; i < width; ++i){// 頂点の位置
point[j][i][0] = float(i * 2 - width + 1)
/ float(height - 1);
point[j][i][1] = float(j * 2 - height + 1)
/ float(height - 1);
point[j][i][2] = gray.at<unsigned char>(j, i) * 0.001f;}}
at<型>() メソッド// 経過時間
const double elaps(getTime() - start);// 経過時間がフレームの時刻に達していたら
if (elaps >= video.get(CV_CAP_PROP_POS_MSEC) * 0.001){// 1フレーム取得する
if (!video.grab())
{
（省略）
}
else
{
// ムービーファイルのフレームの一時保存先
cv::Mat frame;// 位置データを転送する
submitPoint(width, height, point);// 取得したフレームを保存する
video.retrieve(frame);// グレースケール化
cv::Mat gray;cv::cvtColor(frame, gray, CV_BGR2GRAY);（右に続く）

102

}

}

}

は cv::Mat の要素にアクセスする// 保存したフレームを転送する
submitColor(frame.cols, frame.rows, frame.data);// プログラムを終了しない
return true;⚫ image がグレースケールの場合:
image.at<unsigned char>(j, i)⚫ image がカラー (BGR) の場合:
image.at<cv::Vec3b>(j, i)[0] ･･･ 青image.at<cv::Vec3b>(j, i)[1] ･･･ 緑
image.at<cv::Vec3b>(j, i)[2] ･･･ 赤April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University

実行結果
◼

103

例示したプログラムの一部を拡大しています

April 25, 2019

Visual Media Laboratory | Faculty of Systems Engineering | Wakayama University◼ 「SNOW」みたいなもの

課題7
◼
◼
◼

◼

104

オリジナルなビデオエフェクトを考えて実装してください
技術的なことでわからないことがあれば教員に相談してくださいスクリーンショットを 07.png というファイル名でアップロードしてくださいkadai1.cpp をアップロードしてく
ださい◼ 「スキャニメイト」みたいなもの◼ 「Premier」や「After Effetcts」にあるようなもの◼ その他オリジナルなビデオエフェクト

April 25, 2019