0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

RaspberryPiで顔を検知したら動画を撮る

Posted at

はじめに

こちらはRaspberry Pi と LINE messaging API で作る監視カメラシリーズ第7弾です。

今回はシリーズ第6弾の顔検出の処理を最適化した内容に、顔を検出したら動画を撮る機能へ変更します。

こちらは無限ループするため処理の中断はctrキー + cを押してください。

顔検出したら動画を撮るコードです。

line_botディレクトリに移動し

cd /home/pi/projects/line_bot

main.cppを作成します

vim main.cpp

テキストエディタに次のコードを貼り付けます。

main.cpp
#include <iostream>
#include <opencv2/opencv.hpp>
#include <chrono> // 時間計測用

using namespace std;
using namespace cv;
using namespace std::chrono;

// グローバルでVideoWriterを定義 (録画の開始/停止でオブジェクトを再生成するため)
VideoWriter writer; 

int main() {
    
    // 1. 初期設定と検出器のロード
    
    CascadeClassifier face_detector;
    string cascade_path = "haarcascade_frontalface_default.xml"; 
    
    if (!face_detector.load(cascade_path)) {
        cerr << "エラー: 顔カスケードファイルを読み込めませんでした:" << cascade_path << endl;
        cerr << "'haarcascade_frontalface_default.xml' が実行可能ファイルと同じディレクトリにあること、または絶対パスがパスが合っていることを確認してください。" << endl;
        return -1;
    }
    
    // 2. VideoCaptureオブジェクトの初期化 
    VideoCapture cap(0); 

    if (!cap.isOpened()) {
        cerr << "エラー: カメラを開けませんでした。" << endl;
        return -1;
    }

    // 3. 動画設定の取得 (解像度、FPS)
    int frame_width = static_cast<int>(cap.get(CAP_PROP_FRAME_WIDTH));
    int frame_height = static_cast<int>(cap.get(CAP_PROP_FRAME_HEIGHT));
    Size frame_size(frame_width, frame_height);
    double fps = 30.0; // 録画FPS
    
    // 4. 状態管理変数
    bool is_recording = false;
    // 最後に顔を検知した時刻 (初期値は現在時刻)
    auto last_detection_time = high_resolution_clock::now(); 
    // 録画を継続する時間(秒)
    const seconds RECORD_DURATION(5); 
    
    // 5. メインループ
    Mat frame;
    vector<Rect> current_faces;  
    vector<Rect> last_faces;     
    int frame_count = 0;
    const int detection_interval = 5; // 5フレームに一度だけ検出

    cout << "モニターモードを開始しています。顔検出を待機しています..." << endl;

    while (true) { // 無限ループで監視を続ける
        
        if (!cap.read(frame)) { break; }

        // --- 検出処理の間引き ---
        if (frame_count % detection_interval == 0) {
            
            // 解像度を半分に縮小
            Mat small_frame;
            resize(frame, small_frame, Size(), 0.5, 0.5); 
            
            Mat gray_frame;
            cvtColor(small_frame, gray_frame, COLOR_BGR2GRAY);
            
            // Haar Cascadeのパラメータ設定 
            face_detector.detectMultiScale(gray_frame, current_faces, 1.1, 7, 0, Size(30, 30));
            
            // 検出結果の座標を元画像サイズに戻す
            last_faces.clear();
            for (auto& face : current_faces) {
                face.x *= 2; face.y *= 2; 
                face.width *= 2; face.height *= 2;
                last_faces.push_back(face); 
            }
        } 
        
        //  録画ロジックの核となる部分 
        bool face_detected_this_frame = !last_faces.empty();
        
        // 1. 【検知があった場合】: タイマーをリセットし、録画を開始/継続
        if (face_detected_this_frame) {
            // 最後に検知した時刻を現在時刻に更新(タイマーリセット)
            last_detection_time = high_resolution_clock::now();
            
            if (!is_recording) {
                // 録画が始まっていなければ、新しく開始する
                string filename = "motion_" + to_string(std::time(nullptr)) + ".avi";
                writer.open(filename, VideoWriter::fourcc('M', 'J', 'P', 'G'), fps, frame_size);
                
                if (writer.isOpened()) {
                    is_recording = true;
                    cout << "\n>>> [録画開始] 顔が検出されました。録画先: " << filename << endl;
                }
            }
        }
        
        // 2. 【検知がなかった場合】: タイマーをチェックし、録画を終了
        if (is_recording) {
            auto current_time = high_resolution_clock::now();
            auto time_elapsed = duration_cast<seconds>(current_time - last_detection_time);
            
            if (time_elapsed >= RECORD_DURATION) {
                // 5秒経過したら録画を終了
                writer.release();
                is_recording = false;
                cout << ">>> [記録停止] 前回の検出から5秒が経過しました。" << endl;
            }
        }
        

        // 描画は常に実行
        Scalar color = is_recording ? Scalar(0, 0, 255) : Scalar(0, 255, 0); // 録画中は赤、待機中は緑
        for (const auto& face : last_faces) {
            rectangle(frame, face, color, 2); 
        }
        
        // 録画中の場合、フレームをファイルに書き込む
        if (is_recording) {
            writer.write(frame);
        }
        
        frame_count++;
    }

    // 終了処理(無限ループを`ctr + c`で止めるため実行されません)
    if (writer.isOpened()) { writer.release(); }
    cap.release();
    cout << "プログラムは終了しました。" << endl;

    return 0;
}

ESCキーを押して、:wqを入力してEnterで保存して閉じます。

buildディレクトリに移動し、

cd build

makeコマンドでビルドします。

make

実行ファイルのmain_appが生成されていますので./で実行します。

./main_app

こちらは無限ループするため処理の中断はctrキー + cを押してください。

buildディレクトリ内にmotion_(数字の羅列).aviが生成されているので確認してみてください。

scpコマンドでPCにコピーする方法は下記を参考にどうぞ。

さいごに

これで顔を検知したら動画を撮る機能が完成です。
この機能だけでも簡易監視カメラと言えますね。
これでもopencvの表面的な機能しか使用できていませんが、素晴らしいライブラリだということは理解できます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?