车辆的检测、跟踪和计数

【算法的步骤】
1、首先画出感兴趣区域
2、对进入感兴趣区域的车辆进行前灯的检测,跟踪和计数
感兴趣区域的划分,在视频处理中有着重要应用,在视频中标注感兴趣区域的方法:
车辆的检测、跟踪和计数_第1张图片车辆的检测、跟踪和计数_第2张图片车辆的检测、跟踪和计数_第3张图片

暂停视频或者在视频流的第一帧中,画出感兴趣区域

    #include  
    #include  
    #include  
    using namespace cv;  
    #include  
    #include  
    using namespace std;  
    /*----定义鼠标事件--画矩形区域:作用当两个车灯----*/  
    //第一步:全局变量  
    bool drawing_box = false;  
    bool gotBox =  false;  
    Rect box;  
    Point downPoint;  
    /*
    void mouseRectHandler(int event, int x, int y, int flags, void *param)
    {
        switch (event)
        {
        case CV_EVENT_MOUSEMOVE:
            if (drawing_box)
            {
                box.width = x - box.x;
                box.height = y - box.y;
            }
            break;
        case CV_EVENT_LBUTTONDOWN:
            drawing_box = true;
            box = Rect(x, y, 0, 0);
            break;
        case CV_EVENT_LBUTTONUP:
            drawing_box = false;
            gotBox = true;
            if (box.width < 0)
            {
                box.x += box.width;
                box.width *= -1;
            }
            if( box.height < 0 )
            {
                box.y += box.height;
                box.height *= -1;
            }
            break;
        default:
            break;
        }
    }
    */  

    void mouseRectHandler(int event, int x, int y, int flags, void *param)  
    {  
        switch (event)  
        {  
        case CV_EVENT_MOUSEMOVE:  
            if (drawing_box)  
            {  
                //鼠标的移动到downPoint的右下角  
                if( x >=downPoint.x && y >= downPoint.y)  
                {  
                    box.x = downPoint.x;  
                    box.y = downPoint.y;  
                    box.width = x - downPoint.x;  
                    box.height =  y -  downPoint.y;  
                }  
                //鼠标的移动到downPoint的右上角  
                if( x >= downPoint.x && y <= downPoint.y)  
                {  
                    box.x =  downPoint.x;  
                    box.y = y;  
                    box.width = x - downPoint.x;  
                    box.height = downPoint.y - y;  
                }  
                //鼠标的移动到downPoint的左上角  
                if( x <= downPoint.x && y <= downPoint.y)  
                {  
                    box.x = x;  
                    box.y = y;  
                    box.width = downPoint.x - x;  
                    box.height = downPoint.y - y;  
                }  
                //鼠标的移动到downPoint的左下角  
                if( x <= downPoint.x && y >= downPoint.y)  
                {  
                    box.x = x;  
                    box.y = downPoint.y;  
                    box.width = downPoint.x -x;  
                    box.height = y - downPoint.y;  
                }  
            }  
            break;  

        case CV_EVENT_LBUTTONDOWN:  
            //按下鼠标,代表可以可以开始画矩形  
            drawing_box = true;  
            //记录起点  
            downPoint = Point(x,y);  
            break;  


        case CV_EVENT_LBUTTONUP:  

            //松开鼠标,代表结束画矩形  
            drawing_box = false;  
            gotBox = true;  
            break;  
        default:  
            break;  
        }  
    }  

    int main(int argc,char*argv[])   
    {   
         //读取视频   
         VideoCapture video(argv[1]);   

         //判断视频是否打开   
         if( !video.isOpened())   
             return 0;   

         //视频中的第一帧   
         Mat firstFrame;   

         Mat frame;   

         //读取视频的第一帧   
         video>>frame;   

         //复制到firstFrame中   
         frame.copyTo(firstFrame);   

         //register     
         namedWindow("video",1);   
         setMouseCallback("video",mouseRectHandler,NULL);   

         //画感兴趣区域   
         while(!gotBox)   
         {   
             firstFrame.copyTo(frame);   
             rectangle(frame,box,Scalar(255,0,0),2);//画出感兴趣区域  
             imshow("video",frame);   
             if(waitKey(50) == 'q')//---------很重要   
                 break;   
         }   
         //remove callback   
         setMouseCallback("video",NULL,NULL);   

         //视频继续   
         for(;;)   
         {   
             //读取视频   
             video>>frame;  
              //判断是否有当前帧  
              if(!frame.data)  
                  break;  
              //画出感兴趣区域  
              rectangle(frame,box,Scalar(255,255,0),2);  
              imshow("video",frame);   
              if(waitKey(33) == 'q')   
                  break;   
         }   
         return 0;   
    } 

车辆的检测、跟踪和计数_第4张图片车辆的检测、跟踪和计数_第5张图片车辆的检测、跟踪和计数_第6张图片
框定感兴趣区域之后开始检测,计数,完整代码如下:

#include
using namespace std;
#include
#include
#include
#include
using namespace cv;
//Trackbar控制的变量及该值的最大值
int thresh = 200;
const int MAX_THRESH = 255;
//Trackbar控制的函数
void thresh_callback(int,void*);
int w_h = 25;
const int MAX_W_H = 100;
int vehicleFrequency = 2;//这个值不能过于大,否则拍到的车辆里,没有车牌
const int MAX_VEHICLEFREQUENCY = 15;
//全局标量
Mat frame;//原视频流的帧
Mat grayFrame;//颜色空间转化
Mat binaryFrame;//二值化
Mat kernel;//进行形态学处理的核函数
Rect box_vehicle;
bool vehicleBool;//判断是否有车辆
//存储边缘
vector<vector > contours;
vector hierarchy;
Rect vehicleRect;//通过前灯检测,定位出来的车辆
/*----定义鼠标事件--画矩形区域:作用当两个车灯----*/
//第一步:全局变量
bool drawing_box = false;
bool gotBox =  false;
Rect box;
Point downPoint;
void mouseRectHandler(int event, int x, int y, int flags, void *param)
{
    switch (event)
    {
    case CV_EVENT_MOUSEMOVE:
        if (drawing_box)
        {
            //鼠标的移动到downPoint的右下角
            if( x >=downPoint.x && y >= downPoint.y)
            {
                box.x = downPoint.x;
                box.y = downPoint.y;
                box.width = x - downPoint.x;
                box.height =  y -  downPoint.y;
            }
            //鼠标的移动到downPoint的右上角
            if( x >= downPoint.x && y <= downPoint.y)
            {
                box.x =  downPoint.x;
                box.y = y;
                box.width = x - downPoint.x;
                box.height = downPoint.y - y;
            }
            //鼠标的移动到downPoint的左上角
            if( x <= downPoint.x && y <= downPoint.y)
            {
                box.x = x;
                box.y = y;
                box.width = downPoint.x - x;
                box.height = downPoint.y - y;
            }
            //鼠标的移动到downPoint的左下角
            if( x <= downPoint.x && y >= downPoint.y)
            {
                box.x = x;
                box.y = downPoint.y;
                box.width = downPoint.x -x;
                box.height = y - downPoint.y;
            }
        }
        break;
    case CV_EVENT_LBUTTONDOWN:
        //按下鼠标,代表可以可以开始画矩形
        drawing_box = true;
        //记录起点
        downPoint = Point(x,y);
        break;
    case CV_EVENT_LBUTTONUP:
        //松开鼠标,代表结束画矩形
        drawing_box = false;
        gotBox = true;
        break;
    default:
        break;
    }
}
int main()
{
    //视频流的输入
    VideoCapture video("1.avi");
    if( !video.isOpened())
        return -1;
    //得到形态学处理的kernel
    kernel = getStructuringElement(MORPH_CROSS,Size(3,3),Point(-1,-1));
    //register,注册鼠标事件
    namedWindow("video",CV_WINDOW_AUTOSIZE);
    setMouseCallback("video",mouseRectHandler,NULL);
    //输出视频流,并同时画出感兴趣区域
    for(;;)
    {
        video>>frame;
        if(!frame.data)
            break;
        //控制阈值
        createTrackbar("前灯检测:","video",&thresh,MAX_THRESH,thresh_callback);
        thresh_callback( 0, 0 );//这一行很重要
        //去除面积过小的
        createTrackbar("面积:","video",&w_h,MAX_W_H,thresh_callback);
        thresh_callback( 0, 0 );
        //代表一辆车,如果出现的频数大于
        createTrackbar("频数:","video",&vehicleFrequency,MAX_VEHICLEFREQUENCY,NULL);
        //当得到box时,去除setMouseCallback
        if(gotBox)
        {
            setMouseCallback("video",NULL,NULL);
            break;
        }
        rectangle(frame,box,Scalar(255,0,0),2);//画出感兴趣区域
        imshow("video",frame);
        if(waitKey(33) == 'q')
            break;
    }
    //用于跟踪算法和计数
    bool currentVehicleBool = false;
    bool previousVehicleBool = false;
    int numberVehicle = 0;
    //用于记录每一辆车的的帧数,避免是一闪而过的,可能不是车辆
    int numberVehicleFrame = 1;
    int currentVehicleNumber = 0;
    int previousVehicleNumber  = 0;
    //以上画出感兴趣区域,以下可以判断是否为车辆
    for(;;)
    {
        vehicleRect.width = 0;
        vehicleRect.height = 0;
        video >> frame;
        if(!frame.data)
            break;
        //颜色空间转换
        CV_Assert(frame.channels() == 3);
        cvtColor(frame,grayFrame,COLOR_BGR2GRAY);
        //控制阈值
        createTrackbar("前灯检测:","video",&thresh,MAX_THRESH,thresh_callback);
        thresh_callback( 0, 0 );//这一行很重要
        //去除面积过小的
        createTrackbar("面积:","video",&w_h,MAX_W_H,thresh_callback);
        thresh_callback( 0, 0 );
        createTrackbar("频数:","video",&vehicleFrequency,MAX_VEHICLEFREQUENCY,NULL);
        //画出感兴趣区域
        rectangle(frame,box,Scalar(255,255,0),2);
        //画出检测到的前灯
        rectangle(frame,vehicleRect,Scalar(0,255,0),2);
        //判断vehicleRect和box是否相交,来判断是否有车辆
        box_vehicle = box & vehicleRect;
        //显示二值化
        imshow("二值化",binaryFrame);
        if(box_vehicle.width >0 && vehicleRect.height > 0)
        {
            currentVehicleBool =  true;
            if( previousVehicleBool ==false )
            {
                //代表这是第几辆车
                numberVehicle++;
                //记录当前帧的车辆的标号
                currentVehicleNumber = numberVehicle;
            }
            //用于计数,该辆车出现的帧数
            if( currentVehicleNumber == previousVehicleNumber )
            {
                numberVehicleFrame ++;
                if( numberVehicleFrame == vehicleFrequency+1)
                {
                    cout << "抓拍" << endl;
                    //imshow("抓拍",frame);//保存图片
                }
            }
        }
        else
        {
            currentVehicleBool = false;
            //将归为1,重新计数
            numberVehicleFrame = 1;
        }
        //记录上一帧的是否有车辆
        previousVehicleBool = currentVehicleBool;
        //记录上一帧,车辆的标号(number)
        previousVehicleNumber = currentVehicleNumber;
        //显示图片
        imshow("video",frame);
    cout<if(waitKey(33) == 'q')
            break;
    }
    return 0;
}
void thresh_callback(int,void*)//得到新的vehicleRect
{
    if(!grayFrame.data)
        return;
    //阈值处理
    threshold(grayFrame,binaryFrame,double(thresh),255.0,THRESH_BINARY);
    //均值滤波
    medianBlur(binaryFrame,binaryFrame,5);
    //形态学处理
    morphologyEx(binaryFrame,binaryFrame,MORPH_OPEN,kernel,Point(-1,-1),7,BORDER_REPLICATE);
    //存储边缘
    vector<vector > contours;
    vector hierarchy;
    //找到边缘,注意为什么先把binaryFrame克隆一遍
    Mat tempBinaryFrame = binaryFrame.clone();
    findContours( tempBinaryFrame,contours,hierarchy, CV_RETR_TREE , CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );//注意这个findContours中binaryImage既是输入又是输出
    vector<vector > contours_poly( contours.size() );
    //存储
    vector boundRect;
    boundRect.clear();
    for(int index = 0;index < contours.size(); index++)
    {
        approxPolyDP( Mat(contours[index]), contours_poly[index], 3, true );
        Rect rect =  boundingRect( Mat(contours_poly[index]) );
        //做筛选(去除较小的w_h)
            continue;
        boundRect.push_back(rect);
    }
    //得到整个汽车前灯轮廓
    for(int index = 0;index < boundRect.size() ;index++)
    {
        if(index ==0)
            vehicleRect = boundRect[0];
        else
            vehicleRect = vehicleRect | boundRect[index];//得到最大的矩形
    }
}

车辆的检测、跟踪和计数_第7张图片

你可能感兴趣的:(c++与opencv做图像处理)