USBカメラで撮影をするプログラムを書いてみよう

USBカメラで周りを撮影するプログラム…と聞くとなんだか難しそうですが,OpenCVを使うととても簡単にプログラムを作ることが出来ます.

http://opencv.jp/opencv-2svn/cpp/reading_and_writing_images_and_video.html

手順としては,

  • Webカメラへのアクセス
  • Webカメラから取り込む映像を表示するためのウィンドウの生成
  • 生成したウィンドウの中に映像を描画
  • 全ての処理が終了したらデバイス・メモリの解放

といった感じです.早速実装していきましょう.

プログラムのひな形

作っていくプログラムのひな形です.まずはこれを入力してください.

#include <iostream>

#include <opencv2/opencv.hpp>
#include <opencv2/opencv_lib.hpp>
#include <opencv2/highgui/highgui.hpp>

using namespace cv;
using namespace std;

int main(void)
{
  // このなかにプログラムを書いていきます
}

前の章で「OpenCVで提供される関数にはcvが付く」という話をしました.

しかし,毎回毎回cv::hogehoge~なんて書くのは面倒です.ここでusing namespace cv;というコードをmain関数の外に書いておくと,「cv::」を省くことができます. stdも同じで,std::と頭につく関数の「std::」を省いて書くことができるようになります.

USBカメラへのアクセス

みなさんの手持ちのPCには,インカメラが付いているでしょうか?それともカメラは何も付いていないでしょうか.

インカメラが付いているタイプのものは,自動でインカメラを使うようにOpenCV側がなんとかしてくれます.

インカメラがついていないPCを使っている人は,USBカメラを繋げて下さい.USBカメラが認識されれば,こちらもOpenCVが良きに計らってくれます.

main関数の中に,以下のコードを追加して下さい.

VideoCapture capture(0);
// カメラが使えない場合はプログラムを止める
if(!capture.isOpened())
    return -1;

OpenCVには,VideoCaptureというWebカメラを容易に扱えることのできるクラス(※)が用意されています.

※クラスについては,このページの下に詳しい解説を書いておきました.気になる人は読んでみてください.

VideoCapture capture(0)と書けば,コンピュータがデフォルトで指定しているカメラデバイスにアクセスしてくれます.このときアクセスしたカメラに対して,VideoCaptureクラスの中で定義されている関数を使用することができます.

次のif文では,capture.isOpened()がfalseのとき,プログラムを止めるという処理が書かれています.

否定の条件式はfalseを使うことが多いかもしれませんが、条件の頭に!をつけても否定の条件式になります.覚えておいたほうが良いでしょう.

isOpened()は,VideoCaptureクラスで定義されている関数であり,USBカメラが認識できているかどうかを判断します.

先ほどカメラにアクセスするためにcaptureを定義しましたので,captureでアクセスしたカメラに対してisOpened()関数を使いますよ,という意味でcapture.isOpened()という風に書きます.

カメラの映像を表示してみる

さて,ここまででカメラのアクセスに関するプログラムは書くことができました.しかし,これだけでは本当にカメラの映像が取得できているのかわかりませんね.

実際にカメラの映像を画面上に表示するプログラムを書いてみましょう.先ほどのコードの下に,このようなプログラムを追加してみてください.

Mat frame, src_video;
char windowName[] = "カメラでさるくマップを撮影してね!";
namedWindow(windowName, CV_WINDOW_AUTOSIZE);

while(waitKey(1) == -1){
    capture >> frame;
    src_video = frame;
    imshow(windowName,src_video);
}
destroyAllWindows();
return 0;

サンプルプログラムで出てきたMatを使用しています.ここでは,カメラの映像を画像として描画するためのframeという変数と,frameが取得した映像をコピーして保存しておくためのsrc_videoという変数を定義しています.

これからカメラの映像をもとにプログラムを改造していくのですが,カメラが取得した画像をそのまま加工する事は余りよろしくないため,src_videoを定義して,コピーした映像に対して処理を加えていくことになります.

namedWindow()関数は,ウィンドウを生成するための関数です.引数はそれぞれ(ウィンドウの名前,ウィンドウのサイズ)です.

ここではウィンドウの名前をWindowNameという変数で定義をしています.WindowNameの中身を変更すれば好きな名前にすることができます.

ウィンドウのサイズは数値で細かく指定することもできます.今回のプログラムでは,ウィンドウの大きさを細かく指定する必要がないため,自動で適当なサイズに変形してくれるCV_WINDOW_AUTOSIZEを指定しています.

While文の中身は,

  1. captureで取得したカメラの映像をframeに入力する
  2. src_videoにframeの画像をコピーする
  3. src_videoの画像を,windowNameという名前のウィンドウで表示をする
  4. 1~3の処理を,キーボードのキーが押されるまで繰り返す

という処理を行っています.

C++では,ファイル入出力のための演算子として">>"が定義されています.

captureで取得したデータを,画像データとして出力するために>>演算子を使用しています.

ここまでプログラムを書くことができたら,F5キーを押してプログラムを実行してみましょう.

エラーが出る!

1.msvcp110d.dllがないと言われる

→Releaseモードで実行してみてください

2. OpenCV Error: Assertion failed (size.width>0 && size.height>0) in cv::imshow, file ……というメッセージがコンソール(黒い画面)に表示される

capture >> frameのコード部分を以下のようにdo文で囲ってください

do{
    capture >> frame;
}while(frame.empty());

クラスとは

クラスを扱う前に,C言語の構造体について説明したほうが理解しやすいでしょう.

C言語では,構造体という考え方が存在します.

構造体とは,プログラム中でよく使用するデータ型を定義しておいて,データの入出力を簡単にすることができるデータ構造です.

配列でも似たようなことができますが,配列ではすべてのデータに対し同一のデータ型(int型,double型…)しか使用することができません.

構造体では,構造体で定義したいデータに対して複数のデータ型を与えてあげることができます.

例を見てみましょう.

// 配列
// int型の配列なので,この配列には整数しか入れることができない
int score[5] = {80,73,56,92,64}

// 構造体
// さまざまなデータ型のデータを定義することができる
typedef MEMBER{
    int num;
    char name[100];
    double height;
    double weight;
}

// 構造体の初期化
struct MEMBER nagasaki_yuru{
    {1, "がんばくん", 180.0, 70.5},
    {2, "らんばちゃん", 155.6, 55.1},
    {3, "おむらんちゃん", 165.7, 60.6},
    {4, "人面石くん", 177.5, 80.2},
    {5, "角煮まんじゅうちゃん", 162.3, 62.5},
};

// 構造体の中身を出力するときは以下のように書く
for (int i = 0; i < 5; i++){
    printf("学籍番号:%d,氏名:%15s,身長:%3.1f,体重:%3.1f", nagasaki_yuru[i].num, nagasaki_yuru[i].name, nagasaki_yuru[i].height, nagasaki_yuru[i].weight);
};

構造体のデータ(メンバといいます)を呼び出すとき,nagasaki_yuru[i].numnagasaki_yuru[i].nameのように書いています.

この書き方,capture.isOpened()の書き方と似ていませんか?

クラスとは構造体と非常に似ており,構造体のつくりをさらに拡張し,構造体が持つデータ型の中に関数を含めることができるようにしたものです.

クラスの中に含まれている関数はメンバ関数と呼ばれ,クラス変数名.メンバ関数といった形で呼び出すことができます.

クラスという考え方はC++だけではなく,JavaやC#などのプログラミング言語にも使用されています.