1. 程式人生 > >【opencv學習之四十二】簡單運動檢測

【opencv學習之四十二】簡單運動檢測

視訊運動檢測基本思想是進行每一幀對比,檢測不同然後得出是否運動,一般分為兩種,背景法和差幀法;

背景法是:將一幅圖作為背景,讓後和每一幀對比;缺點是一開始存入的背景可能隨光照變法而造成錯誤,但是可以用在光照環境穩定的地方,優點是可以檢測之前背景沒有的景象;

差幀法是:將前一幀和後一幀進行對比;缺點是無法對運動後突然又靜止的景象進行識別,優點是光照不影響;

例項背景法:

///////////////////////運動檢測,攝像頭檢測,背景法
Mat MoveDetectBack(Mat background,Mat frame);//宣告運動檢測函式
void imgMoveDetectionBack()
{
    /*VideoCapture結構體,儲存影象資訊,open()引數為int index(0為預設攝像頭),讀入攝像頭視訊,
               open()引數為路徑,讀入視訊檔案*/
    VideoCapture cap(0); // 開啟攝像頭0
    if (!cap.isOpened())  // 異常處理
    {
        QMessageBox mesg;
        mesg.about(NULL,"提示","攝像頭開啟失敗");
        waitKey(1000);
    }
    Mat frame;//儲存幀
    Mat background;//儲存背景影象
    Mat result;//儲存結果影象
    
    //先儲存背景
    bool firstF= cap.read(frame); // 先獲取一幀
    //異常處理
    if (!firstF) { QMessageBox mesg;
        mesg.about(NULL,"提示","Cannot read a frame from video stream"); }
    else
    {background = frame.clone();}//儲存背景
    
    //迴圈檢測視訊
    while (1)
    {
        bool bSuccess = cap.read(frame); // 讀取
        if (!bSuccess) //異常處理
        {
            QMessageBox mesg;
            mesg.about(NULL,"提示","Cannot read a frame from video stream");
            break;
        }
        ///////////////////////////////////////////////////////////////////////////
        if (frame.empty())//對幀進行異常檢測
        {
            cout << "frame is empty!" << endl;
            break;
        } 
        //將背景和每一幀做差
        result = MoveDetectBack(background, frame);//呼叫MoveDetect()進行運動物體檢測,返回值存入result
        imshow("result", result);      
        //////////////////////////////////////////////////////////
        if (waitKey(30) == 27) //按鍵退出
        {
            QMessageBox mesg;
            mesg.about(NULL,"資訊","退出攝像");
            break;
        }
    }
    waitKey(10);
    cap.release();
}

//檢測函式
Mat MoveDetectBack(Mat background, Mat frame)
{
    Mat result = frame.clone();
    //1.將background和frame轉為灰度圖
    Mat gray1, gray2;
    cvtColor(background, gray1, CV_BGR2GRAY);
    cvtColor(frame, gray2, CV_BGR2GRAY);
    //2.將background和frame做差
    Mat m_different;
    absdiff(gray1, gray2, m_different);
    imshow("m_different", m_different);
    //3.對差值圖diff_thresh進行閾值化處理
    Mat Dif_Thresh;
    threshold(m_different, Dif_Thresh, 50, 255, CV_THRESH_BINARY);
    imshow("Dif_Thresh",Dif_Thresh);
    //4.腐蝕
    Mat kernel_erode = getStructuringElement(MORPH_RECT, Size(3, 3));
    Mat kernel_dilate = getStructuringElement(MORPH_RECT, Size(15, 15));
    erode(Dif_Thresh, Dif_Thresh, kernel_erode);
    imshow("erode", Dif_Thresh);
    //5.膨脹
    dilate(Dif_Thresh, Dif_Thresh, kernel_dilate);
    imshow("dilate", Dif_Thresh);
    //6.查詢輪廓並繪製輪廓
    vector<vector<Point>> contours;
    findContours(Dif_Thresh, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
    drawContours(result, contours, -1, Scalar(0, 0, 255), 2);//在result上繪製輪廓
    //7.查詢正外接矩形
    vector<Rect> boundRect(contours.size());
    for (int i = 0; i < contours.size(); i++)
    {
        boundRect[i] = boundingRect(contours[i]);
        rectangle(result, boundRect[i], Scalar(0, 255, 0), 2);//在result上繪製正外接矩形
    }
    return result;//返回result
}

效果:






差幀法:

///////////////////////運動檢測3,攝像頭檢測,差幀法
Mat MoveDetect3(Mat background,Mat frame);//宣告運動檢測函式
void imgMoveDetection3()
{
    /*VideoCapture結構體,儲存影象資訊,open()引數為int index(0為預設攝像頭),讀入攝像頭視訊,
               open()引數為路徑,讀入視訊檔案*/
    VideoCapture cap(0); // 開啟攝像頭0
    if (!cap.isOpened())  // 異常處理
    {
        QMessageBox mesg;
        mesg.about(NULL,"提示","攝像頭開啟失敗");
        waitKey(1000);
    }
    Mat frame;//儲存幀
    Mat temp;//儲存前一幀影象
    Mat result;//儲存結果影象
    while (1)
    {
        bool bSuccess = cap.read(frame); // 獲取一幀
        if (!bSuccess) //異常處理
        {
            QMessageBox mesg;
            mesg.about(NULL,"提示","Cannot read a frame from video stream");
            break;
        }
        ///////////////////////////////////////////////////////////////////////////
        if (frame.empty())//對幀進行異常檢測
        {
            cout << "frame is empty!" << endl;
            break;
        }
        if ( temp.empty())//如果temp為空則為第一幀
        {
            result = MoveDetect3(frame, frame);//呼叫MoveDetect()進行運動物體檢測,返回值存入result
        }
        else//若不是第一幀(temp有值了)
        {
            result = MoveDetect3(temp, frame);//呼叫MoveDetect()進行運動物體檢測,返回值存入result
        }
        imshow("result", result);
        temp = frame.clone();
        //////////////////////////////////////////////////////////
        if (waitKey(30) == 27) //按鍵退出
        {
            QMessageBox mesg;
            mesg.about(NULL,"資訊","退出攝像");
            break;
        }
    }
    waitKey(10);
    cap.release();
}

//檢測函式
Mat MoveDetect3(Mat background, Mat frame)
{
    Mat result = frame.clone();
    //1.將background和frame轉為灰度圖
    Mat gray1, gray2;
    cvtColor(background, gray1, CV_BGR2GRAY);
    cvtColor(frame, gray2, CV_BGR2GRAY);
    //2.將background和frame做差
    Mat m_different;
    absdiff(gray1, gray2, m_different);
    imshow("m_different", m_different);
    //3.對差值圖diff_thresh進行閾值化處理
    Mat Dif_Thresh;
    threshold(m_different, Dif_Thresh, 50, 255, CV_THRESH_BINARY);
    imshow("Dif_Thresh",Dif_Thresh);
    //4.腐蝕
    Mat kernel_erode = getStructuringElement(MORPH_RECT, Size(3, 3));
    Mat kernel_dilate = getStructuringElement(MORPH_RECT, Size(15, 15));
    erode(Dif_Thresh, Dif_Thresh, kernel_erode);
    imshow("erode", Dif_Thresh);
    //5.膨脹
    dilate(Dif_Thresh, Dif_Thresh, kernel_dilate);
    imshow("dilate", Dif_Thresh);
    //6.查詢輪廓並繪製輪廓
    vector<vector<Point>> contours;
    findContours(Dif_Thresh, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
    drawContours(result, contours, -1, Scalar(0, 0, 255), 2);//在result上繪製輪廓
    //7.查詢正外接矩形
    vector<Rect> boundRect(contours.size());
    for (int i = 0; i < contours.size(); i++)
    {
        boundRect[i] = boundingRect(contours[i]);
        rectangle(result, boundRect[i], Scalar(0, 255, 0), 2);//在result上繪製正外接矩形
    }
    return result;//返回result
}

效果: