基于opencv的运动物体检测方法总结

今天朋友问我要一个车上充满点点的图片,然后我第一时间想到了光流法,然后想到了之前总结的运动物体检测的几个方法,还在有道云笔记里面,所以打算搬迁过来。

帧间差分法

定义:利用相邻的两帧或者三帧图像,利用像素之间的差异性,判断是否有运动目标

基本步骤:相邻帧相减---阈值处理---去除噪声---膨胀联通---查找轮廓---绘制外接矩形

参考方法:https://www.cnblogs.com/little-monkey/p/7637130.html
#include "opencv2/opencv.hpp"
#include
using namespace std;
using namespace cv;
​
int CarNum = 0;
string intToString(int number)  //int类型转为string类型
{
    stringstream ss;
    ss << number;
    return ss.str();
}
​
Mat MoveDetect(Mat frame1, Mat frame2) {
    Mat result = frame2.clone();
    Mat gray1, gray2;
    cvtColor(frame1, gray1, COLOR_BGR2GRAY);
    cvtColor(frame2, gray2, COLOR_BGR2GRAY);
​
    Mat diff;
    absdiff(gray1, gray2, diff);
    //imshow("absdiss", diff);
    threshold(diff, diff, 50, 255, THRESH_BINARY);
    imshow("threshold", diff);
​
    medianBlur(diff, diff, 5);
    imshow("medianBlur", diff);
​
    Mat element = getStructuringElement(MORPH_RECT, Size(3, 3));
    Mat element2 = getStructuringElement(MORPH_RECT, Size(50, 50));
    erode(diff, diff, element);
    dilate(diff, diff, element2);
    imshow("dilate", diff);
​
    vector> contours;
    vector hierarcy;
    findContours(diff, contours, hierarcy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0));//查找轮廓
    vector>contours_poly(contours.size());
    vector boundRect(contours.size()); //定义外接矩形集合
    //drawContours(img2, contours, -1, Scalar(0, 0, 255), 1, 8);  //绘制轮廓
    int x0 = 0, y0 = 0, w0 = 0, h0 = 0;
    for (int i = 0; i55 && boundRect[i].width<180 && boundRect[i].height>55 && boundRect[i].height<180) {//轮廓筛选
            x0 = boundRect[i].x;
            y0 = boundRect[i].y;
            w0 = boundRect[i].width;
            h0 = boundRect[i].height;
​
            rectangle(result, Point(x0, y0), Point(x0 + w0, y0 + h0), Scalar(0, 255, 0), 2, 8, 0);
            //经过这条线(区间),车辆数量+1
            if ((y0 + h0 / 2 + 1) >= 138 && (y0 + h0 / 2 - 1) <= 142) {
                CarNum++;
            }
        }
        line(result, Point(0, 140), Point(568, 140), Scalar(0, 0, 255), 1, 8);//画红线
        Point org(0, 35);
        putText(result, "CarNum=" + intToString(CarNum), org, FONT_HERSHEY_SIMPLEX, 0.8f, Scalar(0, 255, 0), 2);
    }
    return result;
}
​
int main() {
    VideoCapture cap("out3.avi");
    if (!cap.isOpened()) //检查打开是否成功
        return -1;
    Mat frame;
    Mat tmp;
    Mat result;
    int count = 0;
    while (1) {
        cap >> frame;
        if (frame.empty())//检查视频是否结束
            break;
        else {
            resize(frame,frame,Size(640,480));
            count++;
            if (count == 1)
                result = MoveDetect(frame, frame);
            else result = MoveDetect(tmp, frame);
            imshow("video", frame);
            imshow("result", result);
            tmp = frame.clone();
            if (waitKey(20) == 27)
                break;
        }
    }
    cap.release();
    return 0;
}

背景减弱法原理

定义:用原图像减去背景模型,剩下的就是前景图像,即运动目标

基本步骤:原图--背景--阈值处理---去除噪声(腐蚀滤波)---膨胀连通---查找轮廓---外接矩形

代码实现:BackgroundSubtractor一共给我们提供了三种具体方法,分别是BackgroundSubtractorMOG, BackgroundSubtractorMOG2和BackgroundSubtractorGMG (这是基于基于3.1.0)

这三种方法的具体区别及使用方法可以参考这篇官方文档,但是我在官网中看到4.1.0中,只有BackgroundSubtractorKNNBackgroundSubtractorMOG2这两种方法。

//参考链接:https://blog.csdn.net/qq_32925781/article/details/52878465
// 方法:BackgroundSubtractorMOG2
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/videoio.hpp"
#include 
#include 
#include 
#include 
#include 
​
using namespace cv;
using namespace std;
​
Mat frame; //当前帧
Mat fgMaskMOG2; //通过MOG2方法得到的掩码图像fgmask
Mat segm;      //frame的副本
​
vector > contours;
vector hierarchy;
Ptr pMOG2; //MOG2 Background subtractor
​
//处理输入视频函数定义
void processVideo();
​
int main()
{
    //namedWindow("Original Frame");
    //namedWindow("After MOG2");
    //create Background Subtractor objects
    pMOG2 = createBackgroundSubtractorMOG2();
​
    processVideo();
​
    destroyAllWindows();
    return 0;
}
​
​
void processVideo() {
​
    VideoCapture capture(1); //参数为0,默认从摄像头读取视频
​
    if(!capture.isOpened()){
        cout << "Unable to open the camera! " << endl;
       //EXIT_FAILURE 可以作为exit()的参数来使用,表示没有成功地执行一个程序,其值为1        
        exit(EXIT_FAILURE); 
    }
​
    while( true ){
​
        if(!capture.read(frame)) {
            cout << "Unable to read next frame." << endl;
            exit(0);
        }
​
        //对画面进行一定的缩放,方便处理
        double scale = 1.3;         //缩放比例
        Mat smallImg(frame.rows / scale,frame.cols / scale,CV_8SC1);
        resize(frame, frame, smallImg.size(),0,0,INTER_LINEAR);
​
​
        pMOG2->apply(frame, fgMaskMOG2);    //更新背景模型
        frame.copyTo(segm);             //建立一个当前frame的副本
        findContours(fgMaskMOG2, contours, hierarchy,
                     RETR_TREE, CHAIN_APPROX_SIMPLE,Point(0,0)); //检测轮廓
​
        vector  > contours_poly( contours.size());
        vector  center( contours.size());
        vector  radius( contours.size());
        for( int i = 0; i < contours.size(); i++){
         //findContours后的轮廓信息contours可能过于复杂不平滑,
         //可以用approxPolyDP函数对该多边形曲线做适当近似
            approxPolyDP( Mat(contours[i]), contours_poly[i], 3, true);
            //得到轮廓的外包络圆
            minEnclosingCircle( contours_poly[i], center[i], radius[i]);
        }
        //对所得到的轮廓进行一定的筛选
        for(int i = 0; i < contours.size(); i++ ){
            if (contourArea(contours[i]) > 500){
                circle(segm, center[i], (int)radius[i], Scalar(100, 100, 0), 2, 8, 0);
                break;
            }
        }
​
        //得到当前是第几帧
        stringstream ss;
//        rectangle(frame, cv::Point(10, 2), cv::Point(100,20),
//                  cv::Scalar(255,255,255), -1);
        ss << capture.get(CAP_PROP_POS_FRAMES);
        string frameNumberString = ss.str();
        putText(frame, frameNumberString.c_str(), cv::Point(15, 15),
                FONT_HERSHEY_SIMPLEX, 0.5 , cv::Scalar(0,0,0));
​
        //显示
        imshow("frame", frame);
        imshow("Segm", segm);
        imshow("FG Mask MOG 2", fgMaskMOG2);
​
        int key;
        key = waitKey(5);
        if (key == 'q' || key == 'Q' || key == 27)
            break;
    }
​
    capture.release();
}

光流场法

定义:一般而言,光流是由于场景中前景目标本身的移动、相机的运动,或者两者的共同运动所产生的。

原理:

undefined

/*稀疏光流阀*/
#include 
#include 
#include 
#include 
#include 
#include 
using namespace cv;
using namespace std;
int main(int argc, char **argv)
{
​
    VideoCapture capture("/home/demon/CLionProjects/Radar/cmake-build-debug/110_90.avi");
    if (!capture.isOpened()){
        //error in opening the video input
        cerr << "Unable to open file!" << endl;
        return 0;
    }
    // Create some random colors
    vector colors;
    RNG rng;
    for(int i = 0; i < 100; i++)
    {
        int r = rng.uniform(0, 256);
        int g = rng.uniform(0, 256);
        int b = rng.uniform(0, 256);
        colors.push_back(Scalar(r,g,b));
    }
    Mat old_frame, old_gray;
    vector p0, p1;
    // Take first frame and find corners in it
    capture >> old_frame;
    cvtColor(old_frame, old_gray, COLOR_BGR2GRAY);
    goodFeaturesToTrack(old_gray, p0, 100, 0.3, 7, Mat(), 7, false, 0.04);
    // Create a mask image for drawing purposes
    Mat mask = Mat::zeros(old_frame.size(), old_frame.type());
    while(true){
        Mat frame, frame_gray;
        capture >> frame;
        if (frame.empty())
            break;
        cvtColor(frame, frame_gray, COLOR_BGR2GRAY);
        // 计算光流点
        vector status;
        vector err;
        //设置迭代终止条件
        TermCriteria criteria = TermCriteria((TermCriteria::COUNT) + (TermCriteria::EPS), 10, 0.03);
​
        calcOpticalFlowPyrLK(old_gray, frame_gray, p0, p1, status, err, Size(15,15), 2, criteria);
​
        vector good_new;
        for(uint i = 0; i < p0.size(); i++)
        {
            // Select good points
            if(status[i] == 1) {
                good_new.push_back(p1[i]);
                // draw the tracks
//                line(mask,p1[i], p0[i], Scalar(0,0,255), 2);
//                circle(frame, p1[i], 5, Scalar(0,0,255), -1);
                circle(mask, p1[i], 10, Scalar(0,0,255), -1);
            }
        }
​
        Mat img;
        add(frame, mask, img);
        imshow("Frame", img);
        int keyboard = waitKey(30);
        if (keyboard == 'q' || keyboard == 27)
            break;
        // Now update the previous frame and previous points
        old_gray = frame_gray.clone();
        p0 = good_new;
    }
}

 写视频

写视频
//实现写视频功能
#include
#include
using namespace cv;
using namespace std;
​
int main()
{
//    'M', 'J', 'P', 'G'    'X','V','I','D'
//Size要和图片尺寸保持一致
    VideoWriter writer("blue_red.avi",cv::VideoWriter::fourcc('X','V','I','D'),8,Size(1280,1024),true);
    char filename[50];
    Mat frame;
    for (int i = 1; i < 243; i++)
    {
        sprintf(filename,"//home/demon/MVViewer/6mm-two/%d.bmp",i);
        frame=imread(filename);
        if(frame.empty())   break;
        writer<

参考链接:https://www.cnblogs.com/little-monkey/p/7637130.html

     https://blog.csdn.net/zhang1308299607/article/details/80081553

     http://www.dataguru.cn/thread-926371-1-1.html

     https://blog.csdn.net/oliverkingli/article/details/78067557

 

 

你可能感兴趣的:(opencv,opencv,计算机视觉)