opencv学习之路(37)运动物体检测

Posted 进击的小猴子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了opencv学习之路(37)运动物体检测相关的知识,希望对你有一定的参考价值。

一、运动物体轮廓椭圆拟合及中心

 1 #include "opencv2/opencv.hpp"
 2 #include<iostream>
 3 using namespace std;
 4 using namespace cv;
 5 
 6 Mat MoveDetect(Mat frame1, Mat frame2)
 7 {
 8     Mat result = frame2.clone();
 9     Mat gray1, gray2;
10     cvtColor(frame1, gray1, CV_BGR2GRAY);
11     cvtColor(frame2, gray2, CV_BGR2GRAY);
12 
13     Mat diff;
14     absdiff(gray1, gray2, diff);
15     imshow("absdiss", diff);
16     threshold(diff, diff, 45, 255, CV_THRESH_BINARY);
17     imshow("threshold", diff);
18  
19     Mat element = getStructuringElement(MORPH_RECT, Size(3, 3));
20     Mat element2 = getStructuringElement(MORPH_RECT, Size(25, 25));
21     erode(diff, diff, element);
22     imshow("erode", diff);
23  
24     dilate(diff, diff, element2);
25     imshow("dilate", diff);
26 
27     vector<vector<Point>> contours;
28     vector<Vec4i> hierarcy;
29     //画椭圆及中心
30     findContours(diff, contours, hierarcy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
31     cout<<"num="<<contours.size()<<endl;
32     vector<RotatedRect> box(contours.size());
33     for(int i=0; i<contours.size(); i++)
34     {
35         box[i] = fitEllipse(Mat(contours[i]));
36         ellipse(result, box[i], Scalar(0, 255, 0), 2, 8);    
37         circle(result, box[i].center, 3, Scalar(0, 0, 255), -1, 8);
38     }    
39     return result;    
40 }
41 
42 void main()
43 {     
44     VideoCapture cap("E://man.avi");
45     if(!cap.isOpened()) //检查打开是否成功
46          return;
47     Mat frame;
48     Mat result;
49     Mat background;
50     int count=0;
51     while(1)
52     {
53         cap>>frame;
54         if(frame.empty())
55             break;
56         else{            
57             count++;
58             if(count==1)
59                  background = frame.clone(); //提取第一帧为背景帧
60             imshow("video", frame);
61             result = MoveDetect(background, frame);
62             imshow("result", result);
63             if(waitKey(50)==27)
64                break;
65         }
66     }
67     cap.release();  
68 }

和上一篇文章代码的不同点在30-38行,天台行人视频适合用背景减法处理,自行车视频适合帧差法处理

二、滤波方法去除噪声

 上篇文章中使用腐蚀膨胀消除噪声,这次使用滤波方法去除噪声

中值滤波

  //二值化后使用中值滤波+膨胀
  Mat element = getStructuringElement(MORPH_RECT, Size(11, 11)); medianBlur(diff, diff, 5);//中值滤波 imshow("medianBlur", diff); dilate(diff, diff, element); imshow("dilate", diff);

均值滤波

#include "opencv2/opencv.hpp"
#include<iostream>
using namespace std;
using namespace cv;

//int to string helper function
string intToString(int number)
{
    stringstream ss;
    ss << number;
    return ss.str();
}

Mat MoveDetect(Mat background, Mat img)
{
    Mat result = img.clone();
    Mat gray1, gray2;
    cvtColor(background, gray1, CV_BGR2GRAY);
    cvtColor(img, gray2, CV_BGR2GRAY);

    Mat diff;
    absdiff(gray1, gray2, diff);
    threshold(diff, diff, 20, 255, CV_THRESH_BINARY);
    imshow("threshold", diff);
    blur(diff, diff, Size(10, 10));//均值滤波
    imshow("blur", diff);

    vector<vector<Point>> contours;  
    vector<Vec4i> hierarcy;
    findContours(diff, contours, hierarcy, CV_RETR_EXTERNAL, CHAIN_APPROX_NONE); //查找轮廓
    vector<Rect> 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; i<contours.size(); i++)
    {
        boundRect[i] = boundingRect((Mat)contours[i]); //查找每个轮廓的外接矩形
    
        x0 = boundRect[i].x;  //获得第i个外接矩形的左上角的x坐标
        y0 = boundRect[i].y; //获得第i个外接矩形的左上角的y坐标
        w0 = boundRect[i].width; //获得第i个外接矩形的宽度
        h0 = boundRect[i].height; //获得第i个外接矩形的高度
        //rectangle(result, Point(x0, y0), Point(x0+w0, y0+h0), Scalar(0, 255, 0), 2, 8); //绘制第i个外接矩形
        circle(result, Point(x0+w0/2, y0+h0/2), 15, Scalar(0, 255, 0), 2, 8);
        line(result, Point(x0+w0/2-15, y0+h0/2), Point(x0+w0/2+15, y0+h0/2), Scalar(0, 255, 0), 2, 8);
        line(result, Point(x0+w0/2, y0+h0/2-10), Point(x0+w0/2, y0+h0/2+15), Scalar(0, 255, 0), 2, 8);
        putText(result,"(" + intToString(x0+w0/2)+","+intToString(y0+h0/2)+")",Point(x0+w0/2+15, y0+h0/2), 1, 1,Scalar(255,0,0),2);
    }
    return result;
}

void main()
{     
    VideoCapture cap("E://ball.avi");
    if(!cap.isOpened()) //检查打开是否成功
         return;
    Mat frame;
    Mat result;
    Mat background;
    int count=0;
    while(1)
    {
        cap>>frame;
        if(frame.empty())
            break;
        else{            
            count++;
            if(count==1)
                 background = frame.clone(); //提取第一帧为背景帧
            imshow("video", frame);
            result = MoveDetect(background, frame);
            imshow("result", result);
            if(waitKey(50)==27)
               break;
        }
    }
    cap.release();  
}

三、轮廓筛选去除噪声(效果挺好的)

    //其余代码相同
    int x0=0, y0=0, w0=0, h0=0;
    for(int i=0; i<contours.size(); i++)
    {
        boundRect[i] = boundingRect((Mat)contours[i]); //查找每个轮廓的外接矩形
        
        x0 = boundRect[i].x;  //获得第i个外接矩形的左上角的x坐标
        y0 = boundRect[i].y; //获得第i个外接矩形的左上角的y坐标
        w0 = boundRect[i].width; //获得第i个外接矩形的宽度
        h0 = boundRect[i].height; //获得第i个外接矩形的高度
        //筛选
        if(w0>30 && h0>30)
            rectangle(result, Point(x0, y0), Point(x0+w0, y0+h0), Scalar(0, 255, 0), 2, 8); //绘制第i个外接矩形
    }    

四、运动轨迹绘制

#include "opencv2/opencv.hpp"
#include<iostream>
using namespace std;
using namespace cv;

Point center;
Point fre_center;//存储前一帧中心坐标
int num=0;
vector<Point> points;

Mat MoveDetect(Mat background, Mat img)
{
    Mat result = img.clone();
    Mat gray1, gray2;
    cvtColor(background, gray1, CV_BGR2GRAY);
    cvtColor(img, gray2, CV_BGR2GRAY);

    Mat diff;
    absdiff(gray1, gray2, diff);
    imshow("absdiss", diff);
    threshold(diff, diff, 45, 255, CV_THRESH_BINARY);
    imshow("threshold", diff);
 
    Mat element = getStructuringElement(MORPH_RECT, Size(1, 1));
    Mat element2 = getStructuringElement(MORPH_RECT, Size(9, 9));
    erode(diff, diff, element);
    imshow("erode", diff);
    dilate(diff, diff, element2);
    imshow("dilate", diff);

    vector<vector<Point>> contours;  
    vector<Vec4i> hierarcy;
    findContours(diff, contours, hierarcy, CV_RETR_EXTERNAL, CHAIN_APPROX_NONE); //查找轮廓
    vector<Rect> boundRect(contours.size()); //定义外接矩形集合
    //drawContours(img2, contours, -1, Scalar(0, 0, 255), 1, 8);  //绘制轮廓
    vector<RotatedRect> box(contours.size());
    int x0=0, y0=0, w0=0, h0=0;
    for(int i=0; i<contours.size(); i++)
    {
        boundRect[i] = boundingRect((Mat)contours[i]); //查找每个轮廓的外接矩形
        
        x0 = boundRect[i].x;  //获得第i个外接矩形的左上角的x坐标
        y0 = boundRect[i].y; //获得第i个外接矩形的左上角的y坐标
        w0 = boundRect[i].width; //获得第i个外接矩形的宽度
        h0 = boundRect[i].height; //获得第i个外接矩形的高度
        if(w0>30 && h0>30)//筛选长宽大于30的轮廓
        {
            num++;
            //rectangle(result, Point(x0, y0), Point(x0+w0, y0+h0), Scalar(0, 255, 0), 2, 8); //绘制第i个外接矩形
            box[i] = fitEllipse(Mat(contours[i]));
            ellipse(result, box[i], Scalar(255, 0, 0), 2, 8);            //椭圆轮廓
            circle(result, box[i].center, 3, Scalar(0, 0, 255), -1, 8); //画中心
            center = box[i].center;//当前帧的中心坐标
            points.push_back(center);//中心塞进points向量集
            if(num !=1)
            {
                //line(result, fre_center, center, Scalar(255, 0, 0), 2, 8);
                for(int j=0; j<points.size()-1; j++)
                    line(result, points[j], points[j+1], Scalar(0, 255, 0), 2, 8);
            }
            //fre_center = center;
        }
    }
    return result;
}

void main()
{     
    VideoCapture cap("E://man.avi");
    if(!cap.isOpened()) //检查打开是否成功
         return;
    Mat frame;
    Mat background;
    Mat result;
    int count=0;
    while(1)
    {
        cap>>frame;
        if(!frame.empty())
        {
            count++;
            if(count==1)
                background = frame.clone(); //提取第一帧为背景帧
            imshow("video", frame);
            result = MoveDetect(background, frame);
            imshow("result", result);
            if(waitKey(30)==27)
               break;
        }
        else
            break;
    }
    cap.release();  
}

五、车辆数量检测

1.帧差法检测运动目标

2.预处理:a.转灰度图,绝对值做差  b.二值化,腐蚀,中值滤波,膨胀  c.查找轮廓,筛选轮廓,绘制外接矩形,计数,输出

 1 #include "opencv2/opencv.hpp"
 2 #include<iostream>
 3 using namespace std;
 4 using namespace cv;
 5 
 6 int CarNum = 0;
 7 //int to string helper function
 8 string intToString(int number)
 9 {
10     //this function has a number input and string output
11     stringstream ss;
12     ss << number;
13     return ss.str();
14 }
15 
16 Mat MoveDetect(Mat frame1, Mat frame2) {
17     Mat result = frame2.clone();
18     Mat gray1, gray2;
19     cvtColor(frame1, gray1, CV_BGR2GRAY);
20     cvtColor(frame2, gray2, CV_BGR2GRAY);
21 
22     Mat diff;
23     absdiff(gray1, gray2, diff);
24     //imshow("absdiss", diff);
25     threshold(diff, diff, 25, 255, CV_THRESH_BINARY);
26     imshow("threshold", diff);
27 
28     Mat element = getStructuringElement(MORPH_RECT, Size(3, 3));
29     Mat element2 = getStructuringElement(MORPH_RECT, Size(19, 19));
30     erode(diff, diff, element);
31     //imshow("erode", dst);
32     medianBlur(diff, diff, 3);
33     imshow("medianBlur", diff);
34     dilate(diff, diff, element2);
35     imshow("dilate", diff);
36 
37     vector<vector<Point>> contours;
38     vector<Vec4i> hierarcy;
39     findContours(diff, contours, hierarcy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));//查找轮廓
40     vector<vector<Point>>contours_poly(contours.size());
41     vector<Rect> boundRect(contours.size()); //定义外接矩形集合
42                                              //drawContours(img2, contours, -1, Scalar(0, 0, 255), 1, 8);  //绘制轮廓
43     int x0 = 0, y0 = 0, w0 = 0, h0 = 0;
44     for (int i = 0; i<contours.size(); i++)
45     {
46         approxPolyDP(Mat(contours[i]), contours_poly[i], 3, true);//对图像轮廓点进行多边形拟合:轮廓点组成的点集,输出的多边形点集,精度(即两个轮廓点之间的距离),输出多边形是否封闭
47         boundRect[i] = boundingRect(Mat(contours_poly[i]));
48         if (boundRect[i].width>55 && boundRect[i].width<180 && boundRect[i].height>55 && boundRect[i].height<180) {//轮廓筛选
49             x0 = boundRect[i].x;
50             y0 = boundRect[i].y;
51             w0 = boundRect[i].width;
52             h0 = boundRect[i].height;
53 
54             rectangle(result, Point(x0, y0), Point(x0 + w0, y0 + h0), Scalar(0, 255, 0), 2, 8, 0);
55             if ((y0 + h0 / 2 + 1) >= 138 && (y0 + h0 / 2 - 1) <= 142) {//经过这条线(区间),车辆数量+1
56                 CarNum++;
57             }
58         }
59         line(result, Point(0, 140), Point(568, 140), Scalar(0, 0, 255), 1, 8);//画红线
60         Point org(0, 35);
61         putText(result, "CarNum=" + intToString(CarNum), org, CV_FONT_HERSHEY_SIMPLEX, 0.8f, Scalar(0, 255, 0), 2);
62     }
63     return result;
64 }
65 
66 void main()
67 {
68     VideoCapture cap("E://2.avi");
69     if (!cap.isOpened()) //检查打开是否成功
70         return;
71     Mat frame;
72     Mat tmp;
73     Mat result;
74     int count = 0;
75     while (1)
76     {
77         cap >> frame;
78         if(frame.empty())//检查视频是否结束
79             break;
80         else{
81             count++;
82             if (count == 1)    
83                 result = MoveDetect(frame, frame);
84             else    result = MoveDetect(tmp, frame);
85             imshow("video", frame);
86             imshow("result", result);
87             tmp = frame.clone();
88             if (waitKey(20) == 27)        
89                 break;
90         }
91     }
92     cap.release();
93 }

 

以上是关于opencv学习之路(37)运动物体检测的主要内容,如果未能解决你的问题,请参考以下文章

opencv 物体运动检测,摄像头灯亮,却没有画面黑屏

opencv运动车里检测跟踪轨迹不对

opencv 如何检测特定形状的物体

使用 opencv 仅检测缓慢移动的物体

如何跟踪运动物体的轨迹openCV C++

求基于OpenCV的三帧差分算法代码