モーション検出器、またはOpenCVの作成-それはただ

会社の名前を正当化する必要があります-少なくともビデオに関連する何かをするために。 前のトピックによると、私たちはやかんを作っているだけでなく、スマートホームの「スマートライティング」を見ていることを理解できます。 今週、コンピュータービジョンを操作するためのアルゴリズムとライブラリのセットであるOpenCVの選択に忙しかった。 画像内のオブジェクト、文字認識、すべてのジャズを検索します。



実際、その中で何かをすることは、プログラマーでなくてもそれほど難しい作業ではありません。 それでは、その方法を説明します。



私はすぐに言わなければなりません:記事には、あなたを怖がらせるか、終末まで眠りを奪う恐ろしいビドローコッドがあります。



まだ恐れていない場合は、さらに歓迎します。



はじめに

実際、アイデアは何でしたか。 私は、光を手動で含めることを完全になくしたかったのです。 私たちにはアパートがあり、そこを移動する人がいます。 人々には光が必要です。 アパートの他のすべてのアイテムは光を必要としません。 オブジェクトは移動しませんが、人々は移動します(移動しない場合、彼は死亡するか眠ります。死者と眠っている人も光を必要としません)。 したがって、何らかの動きが観察されるアパート内の場所のみを照らす必要があります。 動きは止まりました-30分または1時間でライトを消すことができます。

動きを判断する方法は?



センサーについて

このような検出器で検出できます:



彼らはそれらをPIRと呼びます-パッシブ赤外線センサー。 または受動的ではなく、焦電性です。 要するに、実際には、サーマルイメージャーの単一ピクセルに基づいています。これは、遠赤外線を受信すると信号を発するまさにそのセルです。



信号が劇的に変化した場合にのみインパルスを与える後の単純な回路-したがって、熱いやかんに信号を送ることはありませんが、動く暖かい物体に信号を送ります。

このような検出器はアラームの99%にインストールされており、すべてのアラームを検出したと思われます。これらは天井の下にあるものです。



やはり同じことですが、ハーネスを使用すると、非接触温度計-額または耳の数秒で温度を測定する温度計ではより困難になります。



そして、高温計では、同じ温度計ですが、より広い範囲で:



気が散りましたが。 もちろん、そのようなセンサーは良いことです。 しかし、彼らはマイナスを持っています-それは、それが起こった場所を指定せずに、観測の範囲全体で動きを示します-近く、遠く。 そして、私には大きな部屋があります。 そして、私は人が働く部分でのみライトをつけたいです。 もちろん、このようなセンサーを約5個設置することは可能ですが、この考えを放棄しました。1台のカメラでほぼ同じ量で済むなら、なぜセンサーをたくさん設置するのでしょうか。



そうですね、OpenCVを選択したかったのです。 それで、私はビンの中にカメラを見つけて、1枚のボード(A20のCubieBoard2 )を取り去りました。



設置

当然、OpenCVを使用するには、最初にインストールする必要があります。 最近のほとんどのシステム(* nixについて話している)では、apt-get install opencvなどの単一のコマンドでインストールされます。 しかし、私たちは簡単な方法で行きますよね? そして、例えば、私が使用しているシングルプレイヤー用のシステムではそうではありません。

包括的なインストールガイドはこちらにありますので、 ここでは詳しく説明しません。

cmakeとGTKを配置しました(明確な良心でapt-get install cmake libgtk2.0-devをインストールしました)。

オフサイトに行き、最新バージョンをダウンロードします。 ただし、RobocraftマニュアルのリンクからSourceForgeを使用する場合、最新バージョン(2.4.6.1)ではなく、v4.6l2を介したカメラからの画像受信が予期せず機能する2.4.6をダウンロードします。 私はこれを知りませんでしたので、4日間、このバージョンを動作させようとしました。 彼らがどこかに書いただけなら。

さらに-標準として:
tar -xjf OpenCV-*.tar.bz2 && cd OpenCV-* && cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local ./ && make && make install
      
      





キットに付属のサンプルを収集できます。

 cd samples/c/ && chmod +x build_all.sh && ./build_all.sh
      
      





実際、私のコードの大部分はmotemplと呼ばれる例から取られています-これはまさに、フレーム内の動き検出機能を実装するプログラムです。 次のようになります。





ドピルカ

動作しますが、それを適用してライトをオンにする方法はありますか? 画面上の動きを示しますが、私たちの場所で照明を制御するコントローラーについてそれを知る必要があります。 また、ポイントの座標ではなく、ライトを点灯する必要がある場所を認識しないことが望ましいです。



まず、このことの仕組みを少し理解します。 ウィンドウ内のカメラからのビデオを表示するために、多くは必要ありません:



 #include <cv.h> #include <highgui.h> #include <stdlib.h> #include <stdio.h> int main(int argc, char* argv[]) { CvCapture* capture = cvCaptureFromCAM(0);//   CvCapture(   ,      ),   capture.       cvCaptureFromCAM, 0    ,        . IplImage* image = cvQueryFrame( capture ); //    ( image)        cvNamedWindow("image window", 1); //    image window for(;;) //    { image = cvQueryFrame( capture ); //         image cvShowImage("image window", image);//   (image window)   ,       cvWaitKey(10); // 10   .    ,      .   ,  -,    ,   . } }
      
      





このプログラムをtest.cファイルにコピーして、次のようにコンパイルできます。

 gcc -ggdb `pkg-config --cflags opencv` -o `basename test.c .c` test.c `pkg-config --libs opencv`
      
      



繰り返しますが、正直に言うと、このチームが何をしているのかよくわかりません。 よく集めます。 なぜこれなのか?



カメラのビデオが開始され、表示されます。 しかし、そこから抜け出すことさえできません。プログラムは無限ループに陥り、Ctrl + Cだけがその意味のない生活を中断します。 ボタンハンドラーを追加します。



 char c = cvWaitKey(10); //         . if (c == 113 || c == 81) //,   . 113  81 -    "q" -     . { cvReleaseCapture( &capture ); //      . cvDestroyWindow("capture"); //  ,    ! return 0; //  . }
      
      







FPSカウンター:



 CvFont font; //  "" cvInitFont(&font, CV_FONT_HERSHEY_COMPLEX_SMALL, 1.0, 1.0, 1,1,8); //   -  , ,  struct timeval tv0; //-   . int fps=0; int fps_sec=0; char fps_text[2]; int now_sec=0;//  ... gettimeofday(&tv0,0); //   now_sec=tv0.tv_sec; //    if (fps_sec == now_sec) //,      ,      { fps++; // ,     (    ,   .) } else { fps_sec=now_sec; //  ,    snprintf(fps_text,254,"%d",fps); //    FPS fps=0; //   } cvPutText(image, fps_text, cvPoint(5, 20), &font, CV_RGB(255,255,255));//   (image)     520,  ,  ,    , ,     .
      
      







プログラムの全文
 #include "opencv2/video/tracking.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc_c.h" #include <time.h> #include <stdio.h> #include <ctype.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include <errno.h> int main(int argc, char** argv) { IplImage* image = 0; CvCapture* capture = 0; struct timeval tv0; int fps=0; int fps_sec=0; int now_sec=0; char fps_text[2]; CvFont font; cvInitFont(&font, CV_FONT_HERSHEY_COMPLEX_SMALL, 1.0, 1.0, 1,1,8); capture = cvCaptureFromCAM(0); cvNamedWindow( "Motion", 1 ); for(;;) { IplImage* image = cvQueryFrame( capture ); gettimeofday(&tv0,0); now_sec=tv0.tv_sec; if (fps_sec == now_sec) { fps++; } else { fps_sec=now_sec; snprintf(fps_text,254,"%d",fps); fps=0; } cvPutText(image, fps_text, cvPoint(5, 20), &font, CV_RGB(255,255,255)); cvShowImage( "Motion", image ); if( cvWaitKey(10) >= 0 ) break; } cvReleaseCapture( &capture ); cvReleaseImage(&image); cvDestroyWindow( "Motion" ); return 0; }
      
      







これで、カメラからのビデオを表示するプログラムができました。 動きを決定する必要がある画面の部分を何らかの方法で示す必要があります。 それらをピクセル単位で設定するペンではありません。



 int dig_key=0;//,    int region_coordinates[10][4]; // ,     . ... char c = cvWaitKey(20); //         . if (c <=57 && c>= 48) //,       { dig_key=c-48; //key "0123456789" // ,      . } cvSetMouseCallback( "Motion", myMouseCallback, (void*) image); //,      myMouseCallback  ,      Motion    image if (region_coordinates[dig_key][0] != 0 && region_coordinates[dig_key][1] != 0 && region_coordinates[dig_key][2] == 0 && region_coordinates[dig_key][3] == 0) // .        -     . cvRectangle(image, cvPoint(region_coordinates[dig_key][0],region_coordinates[dig_key][1]), cvPoint(region_coordinates[dig_key][0]+1,region_coordinates[dig_key][1]+1), CV_RGB(0,0,255), 2, CV_AA, 0 ); if (region_coordinates[dig_key][0] != 0 && region_coordinates[dig_key][1] != 0 && region_coordinates[dig_key][2] != 0 && region_coordinates[dig_key][3] != 0) //       -   . cvRectangle(image, cvPoint(region_coordinates[dig_key][0],region_coordinates[dig_key][1]), cvPoint(region_coordinates[dig_key][2],region_coordinates[dig_key][3]), CV_RGB(0,0,255), 2, CV_AA, 0 ); void myMouseCallback( int event, int x, int y, int flags, void* param) //       ,    { IplImage* img = (IplImage*) param; // . ,       switch( event ){ //      case CV_EVENT_MOUSEMOVE: break; //     .  , ,      : printf("%dx %d\n", x, y); case CV_EVENT_LBUTTONDOWN: //     if (region_coordinates[dig_key][0] != 0 && region_coordinates[dig_key][1] != 0 && region_coordinates[dig_key][2] == 0 && region_coordinates[dig_key][3] == 0) //   (    -      ),       -       { region_coordinates[dig_key][2]=x; //dig_key - ,    .      . region_coordinates[dig_key][3]=y; } if (region_coordinates[dig_key][0] == 0 && region_coordinates[dig_key][1] == 0)//   (     ),      . { region_coordinates[dig_key][0]=x; region_coordinates[dig_key][1]=y; } break; } }
      
      







仕組みは次のとおりです。



地域はデジタルボタンで切り替えられます。

プログラムの全文
 #include "opencv2/video/tracking.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc_c.h" #include <time.h> #include <stdio.h> #include <ctype.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include <errno.h> int dig_key=0; int region_coordinates[10][4]; void myMouseCallback( int event, int x, int y, int flags, void* param) { IplImage* img = (IplImage*) param; switch( event ){ case CV_EVENT_MOUSEMOVE: //printf("%dx %d\n", x, y); break; case CV_EVENT_LBUTTONDOWN: //printf("%dx %d\n", region_coordinates[dig_key][0], region_coordinates[dig_key][1]); if (region_coordinates[dig_key][0] != 0 && region_coordinates[dig_key][1] != 0 && region_coordinates[dig_key][2] == 0 && region_coordinates[dig_key][3] == 0) { region_coordinates[dig_key][2]=x; region_coordinates[dig_key][3]=y; } if (region_coordinates[dig_key][0] == 0 && region_coordinates[dig_key][1] == 0) { region_coordinates[dig_key][0]=x; region_coordinates[dig_key][1]=y; } break; case CV_EVENT_RBUTTONDOWN: break; case CV_EVENT_LBUTTONUP: break; } } int main(int argc, char** argv) { IplImage* image = 0; CvCapture* capture = 0; struct timeval tv0; int fps=0; int fps_sec=0; int now_sec=0; char fps_text[2]; CvFont font; cvInitFont(&font, CV_FONT_HERSHEY_COMPLEX_SMALL, 1.0, 1.0, 1,1,8); capture = cvCaptureFromCAM(0); cvNamedWindow( "Motion", 1 ); for(;;) { IplImage* image = cvQueryFrame( capture ); gettimeofday(&tv0,0); now_sec=tv0.tv_sec; if (fps_sec == now_sec) { fps++; } else { fps_sec=now_sec; snprintf(fps_text,254,"%d",fps); fps=0; } cvSetMouseCallback( "Motion", myMouseCallback, (void*) image); if (region_coordinates[dig_key][0] != 0 && region_coordinates[dig_key][1] != 0 && region_coordinates[dig_key][2] == 0 && region_coordinates[dig_key][3] == 0) cvRectangle(image, cvPoint(region_coordinates[dig_key][0],region_coordinates[dig_key][1]), cvPoint(region_coordinates[dig_key][0]+1,region_coordinates[dig_key][1]+1), CV_RGB(0,0,255), 2, CV_AA, 0 ); if (region_coordinates[dig_key][0] != 0 && region_coordinates[dig_key][1] != 0 && region_coordinates[dig_key][2] != 0 && region_coordinates[dig_key][3] != 0) cvRectangle(image, cvPoint(region_coordinates[dig_key][0],region_coordinates[dig_key][1]), cvPoint(region_coordinates[dig_key][2],region_coordinates[dig_key][3]), CV_RGB(0,0,255), 2, CV_AA, 0 ); cvPutText(image, fps_text, cvPoint(5, 20), &font, CV_RGB(255,255,255)); cvShowImage( "Motion", image ); char c = cvWaitKey(20); if (c <=57 && c>= 48) { dig_key=c-48; //key "0123456789" } } cvReleaseCapture( &capture ); cvReleaseImage(&image); cvDestroyWindow( "Motion" ); return 0; }
      
      







しかし、プログラムを開始するたびに観測領域を手動で設定することはありませんか? ファイルに保存しましょう。



 FILE *settings_file; FILE* fd = fopen("regions.bin", "rb"); // . "rb" -    if (fd == NULL) { printf("Error opening file for reading\n"); //    FILE* fd = fopen("regions.bin", "wb"); //  if (fd == NULL) { printf("Error opening file for writing\n"); } else { fwrite(region_coordinates, 1, sizeof(region_coordinates), fd); //  -     fclose(fd); //  printf("File created, please restart program\n"); } return 0; } size_t result = fread(region_coordinates, 1, sizeof(region_coordinates), fd); //  if (result != sizeof(region_coordinates)) //        printf("Error size file\n"); //  fclose(fd); //  FILE* fd = fopen("regions.bin", "wb"); // . "wb" -    if (fd == NULL) //    printf("Error opening file for writing\n"); // fwrite(region_coordinates, 1, sizeof(region_coordinates), fd); //    fclose(fd); // 
      
      





たとえば、これらの関数をwおよびrボタンにバインドし、それらをクリックすると、配列を保存して開きます。



ほんの少ししか残っていません-実際には、どの地域で動きが起こったのかという定義です。 実績をmotempl.sのソースに転送し、どこに侵入できるかを見つけます。

モーション検出の位置に円を描く関数は次のとおりです。

 cvCircle( dst, center, cvRound(magnitude*1.2), color, 3, CV_AA, 0 );
      
      







そして、中心の座標は次のように決定されます。

 center = cvPoint( (comp_rect.x + comp_rect.width/2), (comp_rect.y + comp_rect.height/2) );
      
      







この部分にコードを挿入します。

 int i_mass; //   for (i_mass = 0; i_mass <= 9; i_mass++) //     ,       . { if( comp_rect.x + comp_rect.width/2 <= region_coordinates[i_mass][2] && comp_rect.x + comp_rect.width/2 >= region_coordinates[i_mass][0] && comp_rect.y + comp_rect.height/2 <= region_coordinates[i_mass][3] && comp_rect.y + comp_rect.height/2 >= region_coordinates[i_mass][1] ) //,   ,      -. { cvRectangle(dst, cvPoint(region_coordinates[i_mass][0],region_coordinates[i_mass][1]), cvPoint(region_coordinates[i_mass][2],region_coordinates[i_mass][3]), CV_RGB(0,0,255), 2, CV_AA, 0 ); //    ,      , ,     . printf("Detect motion in region %d\n",i_mass); //       } }
      
      







動作します:





出力はコンソールにではなく、UARTに送信するために、ライトを制御する任意のMKリレーに接続します。 プログラムは、領域内の動きを検出し、領域番号をコントローラーに送信し、割り当てられたランプを点灯します。 しかし、それについては次のシリーズで詳しく説明します。



プロジェクトのソースをgithubに投稿しましたが、誰かがエラーを修正してプログラムを改善する時間を見つけても気にしません:

github.com/vvzvlad/motion-sensor-opencv



ティーポットで叙事詩を見逃したくなく、当社の新しい投稿をすべて見たい場合は、購読することができます 画像 会社のページ (購読ボタン)

はい、私は午前5時に再び投稿を書いたので、エラーメッセージを受け入れます。 しかし-PMで。



All Articles