USBウェブカムを利用した多点監視カメラシステムの製作 (ソフト開発編)

USBウェブカムを利用した多点監視カメラシステムの製作  (構想編)  へ戻る

USBウェブカムを利用した多点監視カメラシステムの製作   (構想編2)   へ戻る

USBウェブカムを利用した多点監視カメラシステムの製作  (ソフト使用編)  へ戻る


●プログラム開発のためには、openCVを入手してインストールする必要があります.最新版は2.1らしいですが、私が使ったのはopenCV2.0です.

インストール手順はここで私が書くまでもなく、わかりやすく書かれた こちらのページを参考にしていただくのが吉です.ページの上の方に openCV のボタンがありますので、そっちに移動してください.

なお、openCVのインストールは、openCVのdllを生成するためにvisual studioでビルドしなくちゃいけないので、かなり面倒くさいです.USBウェブカムのサンプルprojectをビルドするまでには、おおよそこんな手順が必要です.  うひ〜

visual studioのインストール
  →  openCVのダウンロードとインストール
    → CMakeのダウンロードとインストール
      → CMakeでproject生成
        →  visual studioでビルドする
          →  openCVの環境変数のPATHを設定
            →  USBウェブカムのサンプルprojectをダウンロード
              →  visual C++ でビルド

●未確認なのですが、無料で使える visual studio 2008 express だと、ビルドできない、なんてことがあるかもしれません.理由は、MFCというwindowsのライブラリが、express版だと入ってないので、openCVがMFCを利用していたら、express版でビルドできないってことになってしまいます.

私が記述したコードでも、MFCを使っているかもしれません.私はどれがMFCなのか頓着せずにコードを書いています.日付処理のコードはMFCなのかしら?????

ちなみに、わたしは、visual studio professional版を使っていますので、この手の悩みにハマってないだけかもしれないのです.もともと、professionalを買った理由が、別件でMFCなしのexpress版で困ったからなのです.

●USBウェブカムのサンプルprojectは、こちらをダウンロード (zip)してください.

解凍して、3_2.sln をダブルクリックして、vc++ projectを開きます.

なんで 3_2 なのかというと、参考にした書籍の、3章のセクション2のサンプルコードを母体にしたというだけの理由です.安易な名付けをして後悔しています.

visual studioの使い方の解説は、割愛します.


ようやくコードの説明です.全コードはこちら

●includeはこんなもんで、下2行はPCのフォルダ構造によるので、各自書き換える必要があります.
Readf.hは、設定ファイルの USBCAM.ini を読み込むために私が書いたコードです.

#include "stdafx.h"
#include <stdlib.h>
#include "Readf.h"
#include <time.h>
#include <stdio.h>
#include <shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
#include "D:\OpenCV2.0\include\opencv\cv.h"
#include "D:\OpenCV2.0\include\opencv\highgui.h"

●openCVのライブラリをここで指定しています.各自のPC環境次第で書き換える必要があります.
これらのファイルは、visual studioでビルドして生成させたファイルですので、openCVをインストールしただけだとVS2008以下は存在しません.debug用とrelease用で別々のlibと使います.

#ifdef _DEBUG
    //Debugモードの場合
    #pragma comment(lib,"D:\\OpenCV2.0\\VS2008\\lib\\debug\\cv200d.lib")
    #pragma comment(lib,"D:\\OpenCV2.0\\VS2008\\lib\\debug\\cxcore200d.lib")
    #pragma comment(lib,"D:\\OpenCV2.0\\VS2008\\lib\\debug\\cvaux200d.lib")
    #pragma comment(lib,"D:\\OpenCV2.0\\VS2008\\lib\\debug\\highgui200d.lib")
#else
    //Releaseモードの場合
    #pragma comment(lib,"D:\\OpenCV2.0\\VS2008\\lib\\release\\cv200.lib")
    #pragma comment(lib,"D:\\OpenCV2.0\\VS2008\\lib\\release\\cxcore200.lib")
    #pragma comment(lib,"D:\\OpenCV2.0\\VS2008\\lib\\release\\cvaux200.lib")
    #pragma comment(lib,"D:\\OpenCV2.0\\VS2008\\lib\\release\\highgui200.lib")
#endif

●USBCAM.ini ファイルに書かれたパラメータを読みます.C言語でベターッとやってるだけなので、特に説明はないです.

 FILE *fp;
 fp=fopen("USBCAM.ini","r");
 if(fp==0){ printf("error: USBCAM.ini not exist.\n"); return 1; }
 while(1)
 {
  int colum;
  colum=qfread(fp);
  printf("%s %s %g",Field[0],Field[1],atof(Field[1]));
  if(colum==0) break;   /* EOF */
  if(strcmp(Field[0],"CameraNo")==0)    CameraNo   = atof(Field[1]);
  if(strcmp(Field[0],"SpeedBackGround")==0)  SpeedBackGround  = atof(Field[1]);
  if(strcmp(Field[0],"ThresholdMoveDetect")==0) ThresholdMoveDetect = atof(Field[1]);
  if(strcmp(Field[0],"DebugMode")==0)    DebugMode   = atof(Field[1])==1 ? TRUE : FALSE ;
  if(strcmp(Field[0],"IntervalFileSave")==0)  IntervalFileSave = atof(Field[1]);
  if(strcmp(Field[0],"FPS")==0)     FPS     = atof(Field[1]);
 }
 fclose(fp);

 ●ビデオ変数宣言+ウインドウ作成+USBウェブカム接続 をします.
デバッグモードでは、平均映像ウインドウと、差分映像ウインドウ も開きます.
ここで重要なのが、cvCaptureFromCAM( ) の引数 CameraNo です.これが USBCAM.init で記述した0か1か2がこの変数に入るというカラクリで、こうしてUSBウェブカム番号とヒモ付けられるわけです.

 cvNamedWindow("入力映像",0);
 if(DebugMode) cvNamedWindow("平均映像",0);
 if(DebugMode) cvNamedWindow("差分映像",0);
 src = cvCaptureFromCAM(CameraNo); // 映像取得(カメラ映像)

●USBウェブカムのフレームサイズを採取します.こういうのはホストPCからウェブカムに向かってフレームサイズを指定するって仕組みなのかと思ってましたが、そうじゃないんですね.
cvQueryFrame() という関数で、USBカメラから1フレームの画像を受け取ります.ハンドシェーク的に処理されているのか、あるいはUSBウェブカムから垂れ流されている画像の一枚を返しているのかは、不明です.

 camera = cvQueryFrame(src); // 初期フレーム取得
 int h, w;
 w = camera->width;   // 映像フレームの幅
 h = camera->height;   // 映像フレームの高さ

●RGB各8bitの画像領域確保.  hとwは初期フレームから採取した値.
 img_back = cvCreateImage(cvSize(w,h), IPL_DEPTH_8U, 3);
 img_diff = cvCreateImage(cvSize(w,h), IPL_DEPTH_8U, 3);

●動き検出用の、32bit浮動小数点数型の配列を3枚確保し、ゼロで埋める.
 mat_back = cvCreateMat(h,w, CV_32FC3);
 mat_diff = cvCreateMat(h,w, CV_32FC3);
 mat_src  = cvCreateMat(h,w, CV_32FC3);
 cvSetZero(mat_back); cvSetZero(mat_diff); cvSetZero(mat_src); // 0で初期化

●画像に日付を表示させるための、フォント定義. 1,1,0,2は、文字の大きさを指定しています.
 CvFont font;
 cvInitFont(&font, CV_FONT_HERSHEY_COMPLEX, 1, 1, 0, 2, CV_AA);

●フレーム処理ループっていうのは、cvQueryFrame( )で、一枚ずつ画像を採取して、その後段で一枚づつ処理してっていうベタなループなんですね.私は、32フレームが一括納品されてそれをバラして処理するのかと思っていたので、意外な平易さに驚きました.
  camera = cvQueryFrame(src); if(camera == NULL) break; // 1フレーム取得

●ここが、openCVの驚きの便利さその1です.
整数画像と、浮動小数点画像に変換した後に、cvAbsDiff() で一発で差分画像すなわち動き検出画像 mat_diff を生成してしまっているわけです.すげぇ便利.
さらに、動き抽出に必要な、時間平均画像の生成も、cvRunningAvg() で一発で作成してくれます.手間要らずとはこういうことですね.引数の SpeedBackGround は、USBCAM.ini で指定したパラメータなわけです.
末尾で、window表示するために整数画像に変換して、動き検出画像 img_diff と、平均画像 img_back を得ています.

  cvConvert(camera, mat_src);  // 入力画像を浮動小数点数型配列に変換
  cvAbsDiff(mat_src, mat_back, mat_diff);  // 差分の計算 diff = src - back
  cvRunningAvg(mat_src, mat_back, SpeedBackGround, 0);  // 平均行列の更新
  cvConvert(mat_diff, img_diff);  // 浮動小数点数型配列を画像に変換
  cvConvert(mat_back, img_back);

●日付時刻をwindowsのサービスから取得し、カメラ画像にスーパーインポーズ表示する.色はピンクです.
  cvPutText(camera,   date, cvPoint(3,26), &font, CV_RGB(255,0,255)); // 文字
  cvPutText(img_back, date, cvPoint(3,26), &font, CV_RGB(255,0,255)); // 文字
  cvPutText(img_diff, date, cvPoint(3,26), &font, CV_RGB(255,0,255)); // 文字

●動き検出のために、動き検出画像を4x4の16分割して、各領域の動き平均値 ave[ ] を計算します.
yが33からスタートしているのは、日付時刻表示領域を動き検出から除外するためです.

  for(int y=33; y<h; y++){
   for(int x=0; x<w; x++){
    int R,G,B;
    R=R(img_diff,x,y); G=G(img_diff,x,y); B=B(img_diff,x,y);
    float BW;
    BW = (R+B+G)/3;
    if(BW>max) max=BW;
    if(BW<min) min=BW;
        :
      中略
        :
    cnt++;
   }
  }< br>  for(inti=0;i<=15;i++) ave[i]/=(cnt/16);

●動きを検出したところに赤いを表示します.USBCAM.ini に記述した ThresholdMoveDetect がここで利用されます.ave[ ] と ThresholdMoveDetect を比較します.cvCircle( ) がを表示する関数です.

  int TH = ThresholdMoveDetect;
  if(ave[0] >TH) cvCircle(img_diff,cvPoint(w*0.5/4,h*0.5/4),5,CV_RGB(255,0,0),-1,CV_AA,0);
               :
            中略
               :
  if(ave[15]>TH) cvCircle(img_diff,cvPoint(w*3.5/4,h*3.5/4),5,CV_RGB(255,0,0),-1,CV_AA,0);

●openCVの驚きの便利さその2です.
赤いがひとつでも点灯していれば、動きアリとみなし MoveDetect=TRUE になります.
前回JPEG保存してから間もない場合は、たとえ動きがあっても保存しません.ここに USBCAM.ini に記述した IntervalFileSave パラメータが利用されます.
cvSaveImage( )で、JPEG保存されます.簡単でいいですね.

   if(MoveDetect){     
          中略
    if( (FileSavedTime==0) || ((time_ms-FileSavedTime)>IntervalFileSave) ){
          中略
     cvSaveImage(directory, camera);
          中略
    }
   }

●画像を表示します.非デバッグモードでは、カメラからの入力映像だけを表示します.デバッグモードでは、平均映像ウィンドウと、差分映像ウィンドウも表示します.
  cvShowImage("入力映像", camera); // 1フレーム表示
  if(DebugMode) cvShowImage("平均映像", img_back);
  if(DebugMode) cvShowImage("差分映像", img_diff);

●ここでフレームレートを決めます.
USBCAM.ini で書いたパラメータ FPS がここで活用されます.引数の (int)(1000.0/FPS) の数式は、FPS を mS に変換しています.FPSは、Frame Per Second の頭文字で、毎秒何コマの意味です.
USBウェブカムの動画処理の場合の一般的な引数は、cvWaitKey(33)にすることが多いです. なぜなら、33というのが次のフレーム処理まで33ms待つという意味であって、1フレーム33msですから、毎秒30コマで画像処理する意味になります.もしも、100にしたら、毎秒10コマで画像処理することになります.
私はかつてビデオデッキを設計していたので、フレームレートをこのように融通無碍に決めてしまうことには本能的にビビッてしまうので、こんな簡単なやり方で良いのかぁという感慨があります.
27の意味は、ASCIIコードで27であるESCキーが押されたらプログラムからexitするためです.

  if(cvWaitKey((int)(1000.0/FPS)) == 27) break; // ESCキーを押した時終了

●exit時のメモリの解放
 cvDestroyAllWindows();
 cvReleaseCapture(&src);
 cvReleaseImage(&img_diff); cvReleaseImage(&img_back); cvReleaseMat(&mat_src);
 cvReleaseMat(&mat_diff); cvReleaseMat(&mat_back);


USBウェブカムを利用した多点監視カメラシステムの製作  (構想編)  へ戻る

USBウェブカムを利用した多点監視カメラシステムの製作   (構想編2)   へ戻る

USBウェブカムを利用した多点監視カメラシステムの製作  (ソフト使用編)  へ戻る

inserted by FC2 system