草庐IT

【OpenCV】“帧差法”实现移动物体的检测(车辆识别)

似末 2024-01-24 原文

目录

一、帧差法

1、概念

2、为什么帧差法可以检测运动的物体? 

二、使用OpenCV配合帧差法实现车辆识别

1、加载视频

2、灰度处理+帧差计算

3、二值化

4、腐蚀

5、膨胀

6、框选出车辆

三、全部代码+实现效果

1、代码

2、车辆检测效果

四、帧差法存在不足之处


一、帧差法

1、概念

        帧差法是一种通过对视频图像序列中 相邻两帧作差分运算 来获得运动目标轮廓的方法,它可以很好地适用于存在多个运动目标和摄像机移动的情况。

        当监控场景中出现异常物体运动时,帧与帧之间会出现较为明显的差别,两帧相减,得到两帧图像 亮度差的绝对值  ,判断它是否大于 阈值 来分析视频或图像序列的运动特性,确定图像序列中有无物体运动。

2、为什么帧差法可以检测运动的物体? 

        😎还记得小时候的葫芦娃动画吗?每个人物其实都是一张剪纸,也被叫做“剪纸动画”,剪一张就是一帧,假设葫芦娃动画为每秒25帧,1秒内连续播放25张不同的剪纸。

        😎因为每一帧之间是有差异的,所以我们可以看到剪纸 动 起来了。

  •  😋博主演示一遍剪纸动画,让大家更直观的感受

博主有以下同一条鲨鱼的不同形态的png图片,使用图片查看来切换显示每一张图片

 👀可以看到鲨鱼动起来了!!!

        😎因此可以通过判断 前后两帧是否相同 ,来判断是否有运动的物体,即通过帧差法来检测运动的物体。

 

        😁所以下面跟着博主来学习使用OpenCV通过帧差法来进行移动车辆的识别。当然不止可以识别车辆,其他移动的物体也可以识别。

二、使用OpenCV配合帧差法实现车辆识别

  • 开发工具

         🔎Qt 5.8.0  +  OpenCV

1、加载视频

  • 通过VideoCapture来加载本地视频,循环读取每一帧并进行显示
int main(int argc, char *argv[])
{
    Mat frame;

    VideoCapture cap("D:/QT-Project/image/carMove.mp4");
    while(cap.read(frame))
    {
        //读取一帧显示一帧
        imshow("frame", frame);

        //延时
        waitKey(25);
    }
    return 0;
}

2、灰度处理+帧差计算

  • 为了提高计算机的运算速度,图像处理前一般将图像转成灰度图

        因为彩色图片是3通道(RGB)24位深度的图像,而灰度图是单通道8位深度的图像,因此处理灰度图比彩色图效率快多了。

  • frontMat为前一帧,afterMat为后一帧
//灰度处理
cvtColor(frontMat, frontGray, CV_BGR2GRAY);
cvtColor(afterMat, afterGray, CV_BGR2GRAY);

//帧差处理 找到帧与帧之间运动的物体差异
absdiff(frontGray, afterGray, diffGray);
  • 效果:原图(左)、处理后(右)

        通过下图可以发现检测是检测出来了,但是画面非常的暗淡(不清晰),因此需要通过二值化来让图像更清晰点。

3、二值化

  • 通过threshold函数将图像二值化

        参数一为原图,参数二为处理后的图,直接将处理后的图覆盖掉原图即可

//二值化:黑白分明 会产生大量白色噪点
threshold(diffGray, diffGray, 25, 255, CV_THRESH_BINARY);
  • 下面是二值化处理过后的效果

        可以发现图像确实是变“清晰”了,因为二值化后的图像只有黑白两种颜色。并且我们还可以发现白色噪点非常多,因为摄像机抖动,风吹树叶等原因,因此还需要通过腐蚀去除掉这些白色噪点

4、腐蚀

  • 概念

        腐蚀是针对图片的二值化数据进行操作的,主要是针对高亮部分。使用算法,将图像的边缘腐蚀掉。作用就是将目标的边缘的“毛刺”踢除掉。

如下图所示: 

  •  通过erode函数将图像进行腐蚀
//腐蚀处理:去除白色噪点 噪点不能完全去除,反而主要物体会被腐蚀的图案都变得不明显
Mat element = cv::getStructuringElement(MORPH_RECT, Size(3, 3));
erode(diffGray, diffGray, element);
  •  下面是腐蚀之后的效果

        😧白色噪点确实是被去除了,但是我们的车辆也被腐蚀的不成车样(内部坑坑洼洼的),所以还需要通过膨胀将车辆进行进一步处理。

5、膨胀

  • 概念

        膨胀是针对图片的二值化数据进行操作的,主要是针对高亮部分。使用算法,将图像的边缘扩大些。作用就是将目标的边缘或者是内部的坑填掉。

如下图所示:

  •  通过dilate函数将图像进行膨胀
//膨胀处理:将白色区域变“胖”
Mat element2 = cv::getStructuringElement(MORPH_RECT, Size(20, 20));

dilate(diffGray, diffGray, element2);
  •  下面是膨胀之后的效果 

        我们的车辆变成一个个大方块了,做到这一步差不多就可以来标记运动的车辆了,只要画矩形将白色大方块框起来即可。

6、框选出车辆

  • 下面是用白色的框框,框选出来的效果

        框选的原理就是找到白色方块最左边的点与最右边的点,得到之间的大小差距(矩形宽),找到白色方块最上边的点与最下边的点,得到之间的大小差距(矩形高)。

        通过宽高即可画出一个把白色方块包含在内的矩形,矩形左上角坐标通过白色方块最上方的值和最左方的值来确定。

  • 我们只要将白色方框改个显眼的颜色,并在原视频的对应位置画出这个框框即可。下面附全部代码。

三、全部代码+实现效果

1、代码

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

//帧差法检测车辆
Mat MoveCheck(Mat &frontMat, Mat &afterMat)
{
    Mat frontGray ,afterGray, diffGray;
    Mat resframe = afterMat.clone();
    //灰度处理
    cvtColor(frontMat, frontGray, CV_BGR2GRAY);
    cvtColor(afterMat, afterGray, CV_BGR2GRAY);
    //imshow("GRAY", frontGray);

    //帧差处理 找到帧与帧之间运动的物体差异
    //缺点:会把其他运动物体也算进来
    absdiff(frontGray, afterGray, diffGray);
    //imshow("absdiff", diffGray);

    //二值化:黑白分明 会产生大量白色噪点
    threshold(diffGray, diffGray, 25, 255, CV_THRESH_BINARY);
    //imshow("diff", diffGray);

    //腐蚀处理:去除白色噪点 噪点不能完全去除,反而主要物体会被腐蚀的图案都变得不明显
    Mat element = cv::getStructuringElement(MORPH_RECT, Size(3, 3));
    erode(diffGray, diffGray, element);
    //imshow("erode", diffGray);

    //膨胀处理:将白色区域变“胖”
    Mat element2 = cv::getStructuringElement(MORPH_RECT, Size(20, 20));
    dilate(diffGray, diffGray, element2);
    //imshow("dilate", diffGray);

    //动态物体标记
    vector<vector<Point>> contours; //保存关键点
    findContours(diffGray, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

    //提取关键点
    vector<vector<Point>> contours_poly(contours.size());
    vector<Rect> boundRect(contours.size());

    int x, y, w, h;
    int num = contours.size();

    for(int i =0; i< num; i++)
    {
        approxPolyDP(Mat(contours[i]), contours_poly[i], 3, true);
        boundRect[i] = boundingRect(Mat(contours_poly[i]));

        x = boundRect[i].x;
        y = boundRect[i].y;
        w = boundRect[i].width;
        h = boundRect[i].height;

        //绘制
        rectangle(resframe, Point(x, y), Point(x+w, y+h), Scalar(0, 255, 0), 4);
    }
    return resframe;
}

int main(int argc, char *argv[])
{
    Mat frame;
    Mat tempframe;
    Mat res;
    int count = 0;

    VideoCapture cap("D:/QT-Project/image/carMove.mp4");
    while(cap.read(frame))
    {
        count++;
        if(count == 1)
        {
            res = MoveCheck(frame, frame);
        }
        else
        {
            res = MoveCheck(tempframe, frame);
        }
        tempframe = frame.clone();
        imshow("frame", frame);//原视频帧
        imshow("res", res);//框选后的视频帧
        waitKey(25);
    }
    return 0;
}

2、车辆检测效果

 

四、帧差法存在不足之处

  • 相机抖动+起风了

        帧差法虽然能够检测出运动的车辆,但是不仅包括车辆,任何运动的物体都会检测出来,就像上图所示,一旦相机抖动或者突然起大风,运动的物体就多了起来(两帧差异的地方很多),因此就会出现上面那种情况。

😘The end ……🔚

原创不易,转载请标明出处。

对您有帮助的话可以一键三连,会持续更新的(嘻嘻)。

有关【OpenCV】“帧差法”实现移动物体的检测(车辆识别)的更多相关文章

  1. ruby - 多次弹出/移动 ruby​​ 数组 - 2

    我的代码目前看起来像这样numbers=[1,2,3,4,5]defpop_threepop=[]3.times{pop有没有办法在一行中完成pop_three方法中的内容?我基本上想做类似numbers.slice(0,3)的事情,但要删除切片中的数组项。嗯...嗯,我想我刚刚意识到我可以试试slice! 最佳答案 是numbers.pop(3)或者numbers.shift(3)如果你想要另一边。 关于ruby-多次弹出/移动ruby​​数组,我们在StackOverflow上找到一

  2. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  3. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  4. ruby-on-rails - 如何重命名或移动 Rails 的 README_FOR_APP - 2

    当我在我的Rails应用程序根目录中运行rakedoc:app时,API文档是使用/doc/README_FOR_APP作为主页生成的。我想向该文件添加.rdoc扩展名,以便它在GitHub上正确呈现。更好的是,我想将它移动到应用程序根目录(/README.rdoc)。有没有办法通过修改包含的rake/rdoctask任务在我的Rakefile中执行此操作?是否有某个地方可以查找可以修改的主页文件的名称?还是我必须编写一个新的Rake任务?额外的问题:Rails应用程序的两个单独文件/README和/doc/README_FOR_APP背后的逻辑是什么?为什么不只有一个?

  5. 报告回顾丨模型进化狂飙,DetectGPT能否识别最新模型生成结果? - 2

    导读语言模型给我们的生产生活带来了极大便利,但同时不少人也利用他们从事作弊工作。如何规避这些难辨真伪的文字所产生的负面影响也成为一大难题。在3月9日智源Live第33期活动「DetectGPT:判断文本是否为机器生成的工具」中,主讲人Eric为我们讲解了DetectGPT工作背后的思路——一种基于概率曲率检测的用于检测模型生成文本的工具,它可以帮助我们更好地分辨文章的来源和可信度,对保护信息真实、防止欺诈等方面具有重要意义。本次报告主要围绕其功能,实现和效果等展开。(文末点击“阅读原文”,查看活动回放。)Ericmitchell斯坦福大学计算机系四年级博士生,由ChelseaFinn和Chri

  6. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  7. Vscode+Cmake配置并运行opencv环境(Windows和Ubuntu大同小异) - 2

    之前在培训新生的时候,windows环境下配置opencv环境一直教的都是网上主流的vsstudio配置属性表,但是这个似乎对新生来说难度略高(虽然个人觉得完全是他们自己的问题),加之暑假之后对cmake实在是爱不释手,且这样配置确实十分简单(其实都不需要配置),故斗胆妄言vscode下配置CV之法。其实极为简单,图比较多所以很长。如果你看此文还配不好,你应该思考一下是不是自己的问题。闲话少说,直接开始。0.CMkae简介有的人到大二了都不知道cmake是什么,我不说是谁。CMake是一个开源免费并且跨平台的构建工具,可以用简单的语句来描述所有平台的编译过程。它能够根据当前所在平台输出对应的m

  8. [Vuforia]二.3D物体识别 - 2

    之前说过10之后的版本没有3dScan了,所以还是9.8的版本或者之前更早的版本。 3d物体扫描需要先下载扫描的APK进行扫面。首先要在手机上装一个扫描程序,扫描现实中的三维物体,然后上传高通官网,在下载成UnityPackage类型让Unity能够使用这个扫描程序可以从高通官网上进行下载,是一个安卓程序。点到Tools往下滑,找到VuforiaObjectScanner下载后解压数据线连接手机,将apk文件拷入手机安装然后刚才解压文件中的Media文件夹打开,两个PDF图打印第一张A4-ObjectScanningTarget.pdf,主要是用来辅助扫描的。好了,接下来就是扫描三维物体。将瓶

  9. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  10. ruby-on-rails - rbenv:从 RVM 移动到 rbenv 后,在 Jenkins 执行 shell 中找不到命令 - 2

    我从Ubuntu服务器上的RVM转移到rbenv。当我使用RVM时,使用bundle没有问题。转移到rbenv后,我在Jenkins的执行shell中收到“找不到命令”错误。我内爆并删除了RVM,并从~/.bashrc'中删除了所有与RVM相关的行。使用后我仍然收到此错误:rvmimploderm~/.rvm-rfrm~/.rvmrcgeminstallbundlerecho'exportPATH="$HOME/.rbenv/bin:$PATH"'>>~/.bashrcecho'eval"$(rbenvinit-)"'>>~/.bashrc.~/.bashrcrbenvversions

随机推荐