• 基于opencv3.0下的运动车辆检测


    在opencv的初等应用上,对运动物体的识别主要有帧差背景差两种方式。

    帧差法主要的原理是当前帧与前一帧作差取绝对值

    背景差主要的原理是当前帧与背景帧作差取绝对值

    在识别运动车辆上主要需要以下9个步骤:

    (1)读取帧(VideoCapture,Mat)

    (2)ROI选定(Rect) 

    (3)平滑处理(GaussianBlur)

    (4)灰度处理(cvtColor,CV_RGB2GRAY)

    (5)帧差或背景差(absdiff)

    (6)二值化(threshold)

    (7)膨胀(dilate)

    (8)腐蚀(erode)

    (9)绘制运动车辆(findContours,rectangle)

    (1)读取帧(外部视频读取:Videocapture;帧读取:Mat

    这个属于比较基础的内容这里不做解释,直接附上源码:

     1 /******************************************************
     2 函数名称: MyClass
     3 函数功能: 初始化
     4 传入参数:
     5 返 回 值:
     6 建立时间: 2018-05-17
     7 修改时间:
     8 建 立 人: 
     9 修 改 人:
    10 其它说明:
    11 ******************************************************/
    12 MyClass::MyClass()
    13 {
    14     //capture.open("F:/code/vsc++/Car_Find/Car_Find/交大七公里交通监控.avi");
    15     capture.open("交大七公里交通监控.avi");
    16     //capture = 0;
    17     if (!capture.isOpened())//判断是否打开视频文件  
    18     {
    19         exit(1);
    20     }
    21     FPS = capture.get(CV_CAP_PROP_FPS);
    22 }
    23 /******************************************************
    24 函数名称: ~MyClass
    25 函数功能: 释放空间
    26 传入参数:
    27 返 回 值:
    28 建立时间: 2018-05-17
    29 修改时间:
    30 建 立 人: 
    31 修 改 人:
    32 其它说明:
    33 ******************************************************/
    34 MyClass::~MyClass()
    35 {
    36     capture.release();
    37 }
    38 /******************************************************
    39 函数名称: play
    40 函数功能: 播放法
    41 传入参数:
    42 返 回 值:
    43 建立时间: 2018-05-17
    44 修改时间:
    45 建 立 人: 
    46 修 改 人:
    47 其它说明:
    48 ******************************************************/
    49 void MyClass::play(){
    50     Mat frame;
    51     namedWindow("播放界面按Esc退出", 0);
    52     cvResizeWindow("播放界面按Esc退出", 600, 500);
    53     while (true)
    54     {
    55         capture >> frame;
    56         if (frame.empty())break;
    57         imshow("播放界面按Esc退出", frame);
    58         if (waitKey(1000.0 / FPS) == 27)//按原FPS显示  
    59         {
    60             cout << "ESC退出!" << endl;
    61             break;
    62         }
    63     }
    64 }

    处理结果:

     

    (2)ROI选定(Rect)

    这里使用Rect进行划ROI(感兴趣区域),画出一个矩阵的ROI。在这里最好的ROI是梯形(减少除了道路外的不必要的干扰)

    这里提下做法:做一个灰度的mask(遮罩层),然后调整mask的形状大小。ps:感兴趣的可以做下。

    (3)平滑处理(处理方法:GaussianBlur)

    这里采用高斯平滑处理,在拍摄视频的时候会受到电流的干扰,但这个干扰时均匀存在的,所以采用高斯平滑处理可以去除电流的干扰。

    方法很简单,源码如下:

     1 /******************************************************
     2 函数名称: getSmooth
     3 函数功能: 平滑处理
     4 传入参数:
     5 返 回 值:
     6 建立时间: 2018-05-17
     7 修改时间:
     8 建 立 人: 
     9 修 改 人:
    10 其它说明:
    11 ******************************************************/
    12 Mat MyClass::getSmooth(Mat frame)
    13 {
    14     Mat img;
    15     GaussianBlur(frame, img, Size(13, 13),2,2);
    16     return img;
    17 }

    处理结果:

    (4)灰度处理(处理方法:cvtColor;核:CV_RGB2GRAY)

    RGB的图对我们的识别会造成一定的干扰或者说增加处理的难度,这里将原帧转换为灰度图像;

    在平滑处理 后,直接调用opencv的cvtColor方法:

     1 /******************************************************
     2 函数名称: getGray
     3 函数功能: 灰度处理
     4 传入参数:
     5 返 回 值:
     6 建立时间: 2018-05-17
     7 修改时间:
     8 建 立 人: 
     9 修 改 人:
    10 其它说明:
    11 ******************************************************/
    12 Mat MyClass::getGray(Mat frame)
    13 {
    14     Mat img;
    15     cvtColor(frame, img, CV_RGB2GRAY);
    16     return img;
    17 }

    处理结果:

    (5)帧差或背景差(处理方法:absdiff)

    不管是帧差和背景差都是需要获取当前帧跟对比帧,其中对比帧的获取帧的作差是处理的关键。

    在获取到处理好的灰度图在进行帧差处理。

    以下为对比帧的获取 的源码:

     1 /******************************************************
     2 函数名称: play
     3 函数功能: 播放帧差法
     4 传入参数:
     5 返 回 值:
     6 建立时间: 2018-05-17
     7 修改时间:
     8 建 立 人: 
     9 修 改 人:
    10 其它说明:
    11 ******************************************************/
    12 void MyClass::play(){
    13     Mat frame,preframe,curframe,result;
    14     namedWindow("播放界面按Esc退出", 0);
    15     //cvResizeWindow("播放界面按Esc退出", 600, 500);
    16     while (true)
    17     {
    18         capture >> frame;
    19         if (frame.empty())break;
    20         if (preframe.empty())preframe = frame.clone();//首帧处理
    21         curframe = frame.clone();
    22         imshow("播放界面按Esc退出", frame);
    23         if (waitKey(1000.0 / FPS) == 27)//按原FPS显示  
    24         {
    25             cout << "ESC退出!" << endl;
    26             break;
    27         }
    28         preframe = frame.clone();//记录当前帧为下一帧的前帧
    29     }
    30 }

    以下为帧差处理的源码:

    /******************************************************
    函数名称: getDiff
    函数功能: 帧差化处理
    传入参数:
    返 回 值:
    建立时间: 2018-05-17
    修改时间:
    建 立 人: 
    修 改 人:
    其它说明:
    ******************************************************/
    Mat MyClass::getDiff(Mat preframe,Mat frame)
    {
        Mat img;
        absdiff(preframe, frame, img);
        return img;
    }

    处理结果:

    这里一个车的灰度轮廓已经可以识别出来了,但是我们的目的是将车辆的轮廓识别处理,为了更加精准,去除不必要的干扰,需要下面的处理。

    (6)二值化(处理方法:threshold,阈值类型:CV_THRESH_BINARY)

     Threshold函数详解,其中CV_THRESH_BINARY:当前点值大于阈值时,取Maxval,也就是第四个参数,下面再不说明,否则设置为0

    由于这些车辆原画偏暗,这里设定阈值为30,处理当前点值大于阈值时,取白色255。

    源码如下:

     1 /******************************************************
     2 函数名称: getEz
     3 函数功能: 二值化处理
     4 传入参数:
     5 返 回 值:
     6 建立时间: 2018-05-17
     7 修改时间:
     8 建 立 人: 
     9 修 改 人:
    10 其它说明:
    11 ******************************************************/
    12 Mat MyClass::getEz(Mat frame)
    13 {
    14     Mat img;
    15     threshold(frame, img,30, 255, CV_THRESH_BINARY);
    16     return img;
    17 }

    处理结果:

    车的黑白轮廓已经显示出来了,不过这里还不是一块整体,所以需要进行膨胀处理。

    ps:有些博文的处理方式是先腐蚀处理后膨胀处理,这个在一定程度上会消除干扰点。但是这里考虑到车辆中有摩托车,如果先腐蚀处理的话会将摩托车给消除掉,造成识别精度不高。

    (7)膨胀(处理方法:dilate)

    膨胀的目的在于将一辆车拼合成一块完整的个体,以达到标识的目的

    opencv提供了dilate的方法进行处理,源码如下:

     1 /******************************************************
     2 函数名称: getPz
     3 函数功能: 膨胀处理
     4 传入参数:
     5 返 回 值:
     6 建立时间: 2018-05-17
     7 修改时间:
     8 建 立 人: 
     9 修 改 人:
    10 其它说明:
    11 ******************************************************/
    12 Mat MyClass::getPz(Mat frame)
    13 {
    14     Mat img;
    15     Mat element=getStructuringElement(MORPH_RECT, Size(11, 30));
    16     dilate(frame, img, element);
    17     return img;
    18 }

    处理结果:

    这时候已经合成一块了,但是如果旁边的车靠的太近的话,会导致多辆车黏合成一块,所以下面做腐蚀处理。

    (8)腐蚀(处理方法:erode)

    腐蚀的目的在于将因为膨胀而导致的黏合,还有非关键点和区域的清除,以达到区分标识的目的。

    opencv提供了erode的方法进行处理,源码如下:

     1 /******************************************************
     2 函数名称: getFs
     3 函数功能: 腐蚀处理
     4 传入参数:
     5 返 回 值:
     6 建立时间: 2018-05-17
     7 修改时间:
     8 建 立 人: 
     9 修 改 人:
    10 其它说明:
    11 ******************************************************/
    12 Mat MyClass::getFs(Mat frame)
    13 {
    14     Mat img;
    15     Mat element = getStructuringElement(MORPH_RECT, Size(10, 16));
    16     erode(frame, img, element);
    17     return img;
    18 }

    处理结果:

    ps:这里截的图不是很好,看不出效果分离的效果。感兴趣的可以下载下源码去调试。

    (9)绘制运动车辆(处理方法:findContours,rectangle)

    findContours函数参数详解

    这里需要将车辆的外围轮廓描绘出来即可,所以findContours采用

    mode取值“CV_RETR_EXTERNAL”,method取值“CV_CHAIN_APPROX_NONE”,即只检测最外层轮廓,并且保存轮廓上所有点;

    rectangle的使用并不难,但是这里要注意的是:

    a.处理的图像是原图像的ROI;

    b.findContours输出的是处理过图像的坐标位置

    所以在使用rectangle的时候,画区域的时候要加上原来ROI的起始位置的坐标,改成原来图像的坐标位置。

    源码如下:

     1 /******************************************************
     2 函数名称: Deal
     3 函数功能: ROI区域处理
     4 传入参数:
     5 返 回 值:
     6 建立时间: 2018-05-17
     7 修改时间:
     8 建 立 人: 
     9 修 改 人:
    10 其它说明:
    11 ******************************************************/
    12 Mat MyClass::Deal(Mat preframe, Mat frame)
    13 {
    14     Mat result = frame.clone();
    15     Mat curimageROI = preframe(Rect(preframe.cols / 5, preframe.rows / 5, preframe.cols / 1.5, preframe.rows / 1.5));
    16     Mat preimageROI = frame(Rect(frame.cols / 5, frame.rows / 5, frame.cols / 1.5, frame.rows / 1.5));
    17 
    18     Mat sm_pre, sm_cur;
    19     sm_pre = getSmooth(preimageROI);
    20     sm_cur = getSmooth(curimageROI);
    21     imshow("平滑处理", sm_cur);
    22 
    23     Mat gray_pre, gray_cur;
    24     gray_pre = getGray(sm_pre);
    25     gray_cur = getGray(sm_cur);
    26     imshow("灰度处理",gray_cur);
    27 
    28     Mat diff;
    29     diff = getDiff(gray_pre, gray_cur);
    30     imshow("帧差处理",diff);
    31 
    32     Mat ez;
    33     ez = getEz(diff);
    34     imshow("二值化处理", ez);
    35 
    36     Mat pz;
    37     pz = getPz(ez);
    38     imshow("膨胀处理", pz);
    39 
    40     Mat fs;
    41     fs = getFs(pz);
    42     imshow("腐蚀处理", fs);
    43 
    44     vector<vector<Point> > contours;
    45     findContours(fs, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
    46     vector<Rect> boundRect(contours.size());
    47     for (int i = 0; i < contours.size(); i++)
    48     {
    49         boundRect[i] = boundingRect(contours[i]);
    50         rectangle(result, Rect(boundRect[i].x + frame.cols/5, boundRect[i].y + frame.rows/5, boundRect[i].width, boundRect[i].height), Scalar(0, 255, 0), 1);//在result上绘制正外接矩形  
    51     }
    52     return result;
    53 }

    处理结果:

    这里效果不是非常好可以勉强识别出车辆轮廓。但作为初级应用是足够的。

    如需要源码请转移至码云:https://gitee.com/cjqbaba/MediaTest/tree/Car_Find进行源码克隆下载

    如有问题请留言评论。转载请注明出处,谢谢。

  • 相关阅读:
    Jane Austen【简·奥斯汀】
    I Like for You to Be Still【我会一直喜欢你】
    Dialogue between Jack and Rose【jack 和 Rose的对话】
    git删除远程.idea目录
    码云初次导入项目(Idea)
    DelayQueue 订单限时支付实例
    eclipse安装spring的插件
    redis安装命令
    log4j详解
    jstree API
  • 原文地址:https://www.cnblogs.com/cjqbaba/p/9055527.html
Copyright © 2020-2023  润新知