当前位置: 首页 > news >正文

[C#]OpenCvSharp使用帧差法或者三帧差法检测移动物体

关于C++版本帧差法可以参考博客

[C++]OpenCV基于帧差法的运动检测-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/FL1768317420/article/details/137397811?spm=1001.2014.3001.5501

我们将参考C++版本转成opencvsharp版本。

帧差法,也叫做帧间差分法,这里引用百度百科上的一段定义:

帧间差分法是一种通过对视频图像序列中相邻两帧作差分运算来获得运动目标轮廓的方法,它可以很好地适用于存在多个运动目标和摄像机移动的情况。当监控场景中出现异常物体运动时,帧与帧之间会出现较为明显的差别,两帧相减,得到两帧图像亮度差的绝对值,判断它是否大于阈值来分析视频或图像序列的运动特性,确定图像序列中有无物体运动。图像序列逐帧的差分,相当于对图像序列进行了时域下的高通滤波。

最简单的帧差法就是二帧差分法,将视频流中的前后两帧图像转换为灰度图像,再经过高斯模糊消除噪声干扰,然后将两帧图像进行相减操作得到两帧图像之间的差异区域,再对差异图像进行二值分割把差异区域作为前景、不变区域作为背景,并且进行开运算操作来消除一些微小干扰。这样,就得到了两帧图像中明显不同的区域,也就是运动的目标物体。下面对上述博客C++版本做解读:

这段C++ OpenCV代码实现了一个简单的运动检测算法,采用两帧差法来识别视频中的运动区域。以下是代码逐段解读:1. 初始化视频捕获器VideoCapture capture;
capture.open("D:\\opencv_c++\\opencv_tutorial\\data\\images\\bike.avi");
这段代码创建了一个VideoCapture对象capture,用于打开和读取视频文件。这里尝试打开位于指定路径的bike.avi视频文件。2. 检查视频是否成功打开if (!capture.isOpened())
{return 0;
}
使用capture.isOpened()检查视频文件是否成功打开。如果未能成功打开(返回false),则立即结束程序并返回值0。3. 定义所需图像变量Mat pre_frame, current_frame, pre_gray, current_gray, pre_gaus, current_gaus;
定义一系列Mat对象(OpenCV中的多通道图像容器),用于存储不同处理阶段的图像数据:pre_frame 和 current_frame 分别存储前一帧和当前帧的彩色图像。
pre_gray 和 current_gray 存储对应的灰度图像。
pre_gaus 和 current_gaus 存储经过高斯模糊处理的灰度图像。
4. 读取第一帧并进行预处理capture.read(pre_frame);
cvtColor(pre_frame, pre_gray, COLOR_BGR2GRAY);
GaussianBlur(pre_gray, pre_gaus, Size(), 5, 5);
首先从视频中读取第一帧到pre_frame。接着,使用cvtColor函数将其转换为灰度图像并存储在pre_gray中。最后,对pre_gray应用高斯模糊(核大小为5x5),结果存放在pre_gaus。5. 循环处理后续帧while (capture.read(current_frame))
{// ... 处理代码 ...
}
进入主循环,每次迭代从视频中读取下一帧至current_frame。当无法再读取到新帧时(即视频播放完毕),循环结束。6. 当前帧预处理cvtColor(current_frame, current_gray, COLOR_BGR2GRAY);
GaussianBlur(current_gray, current_gaus, Size(), 5, 5);
对当前帧执行与第一帧相同的预处理步骤:转换为灰度图像(current_gray)并应用高斯模糊(current_gaus)。7. 计算两帧差分Mat sub_gray, sub_binary, sub_open;
subtract(current_gaus, pre_gaus, sub_gray);
threshold(sub_gray, sub_binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
计算current_gaus与pre_gaus之间的像素差值,结果存储在sub_gray。然后,对sub_gray应用二值化阈值处理(包括Otsu自适应阈值),得到运动区域的二值图像sub_binary。8. 形态学开运算Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5));
morphologyEx(sub_binary, sub_open, MORPH_OPEN, kernel, Point(-1, -1), 1, 0);
创建一个大小为5x5的矩形结构元素kernel。接着,对sub_binary进行形态学开运算(去除小噪声),输出结果保存在sub_open。9. 显示结果imshow("sub_open", sub_open);
imshow("current_frame", current_frame);
使用imshow函数分别显示运动区域检测结果sub_open和当前帧原始彩色图像current_frame。10. 更新前一帧信息cpp
swap(pre_gaus, current_gaus);
使用swap函数交换pre_gaus和current_gaus的内容,使得pre_gaus存储当前帧高斯模糊后的灰度图像,为下一次循环做好准备。11. 检查用户输入以决定是否退出char ch = cv::waitKey(20);
if (ch == 27)
{break;
}
waitKey(20)函数等待用户按键,同时设置超时时间为20毫秒。若在该时间内接收到按键事件,返回按键的ASCII码;否则返回-1。这里检查是否按下Esc键(ASCII码为27),如果是,则跳出循环,结束视频处理。综上所述,这段代码实现了基于两帧差法的运动检测算法,通过对连续视频帧进行灰度化、高斯模糊、差分、二值化、形态学开运算等步骤,提取出运动区域并在窗口中实时显示,同时允许用户按Esc键随时停止处理。

三帧差分法是将连续的三帧图像,分别进行转灰度图、高斯模糊消除噪声干扰,然后进行逐帧相减,也就是后一帧图像减去当前帧图像、当前帧图像减去前一帧图像,从而得到两张差异图像。再将得到的两个差值图像进行与操作,得到共同的差异区域,最后通过开运算操作消除微小干扰。这样就得到了三帧图像间的明显差异区域,也就是运动的目标物体。

而且二帧差分法对于微小运动物体的检测能力比较差,因为如果在两帧图像之间变化太小,就很难被检测出来。而三帧差分法利用连续三帧图像的差异结果,能够提高对微小运动物体的检测能力,同时增强对噪声、光照等因素的抗干扰能力。以下是对C++代码解读:

这段C++ OpenCV代码同样实现了一个基于两帧差法的运动检测算法,但与之前提供的代码相比,它采用了双缓冲机制,即同时保留两前一帧的信息,以增强对运动检测的稳定性。以下是详细解读:1. 初始化视频捕获器cpp
VideoCapture capture;
capture.open("D:\\opencv_c++\\opencv_tutorial\\data\\images\\bike.avi");
创建一个VideoCapture对象capture,用于打开并读取视频文件。这里尝试打开位于指定路径的bike.avi视频文件。2. 检查视频是否成功打开cpp
if (!capture.isOpened())
{return 0;
}
使用capture.isOpened()检查视频文件是否成功打开。如果未能成功打开(返回false),则立即结束程序并返回值0。3. 定义所需图像变量cpp
Mat pre_frame1, pre_frame2, current_frame,pre_gray1, pre_gray2, current_gray,pre_gaus1, pre_gaus2, current_gaus;
定义一系列Mat对象,用于存储不同处理阶段的图像数据:pre_frame1 和 pre_frame2 分别存储最近两帧的彩色图像。
current_frame 存储当前帧的彩色图像。
pre_gray1 和 pre_gray2 存储对应的灰度图像。
current_gray 存储当前帧的灰度图像。
pre_gaus1 和 pre_gaus2 存储最近两帧经过高斯模糊处理的灰度图像。
current_gaus 存储当前帧经过高斯模糊处理的灰度图像。
4. 读取前两帧并进行预处理cpp
capture.read(pre_frame1);
capture.read(pre_frame2);cvtColor(pre_frame1, pre_gray1, COLOR_BGR2GRAY);
cvtColor(pre_frame2, pre_gray2, COLOR_BGR2GRAY);GaussianBlur(pre_gray1, pre_gaus1, Size(), 10, 0);
GaussianBlur(pre_gray2, pre_gaus2, Size(), 10, 0);
从视频中读取前两帧分别存入pre_frame1和pre_frame2。对这两帧进行灰度化处理后分别存储在pre_gray1和pre_gray2,接着对灰度图像应用高斯模糊(核大小为10x10),结果分别存放在pre_gaus1和pre_gaus2。5. 主循环处理后续帧cpp
while (capture.read(current_frame))
{// ... 处理代码 ...
}
进入主循环,每次迭代从视频中读取下一帧至current_frame。当无法再读取到新帧时(即视频播放完毕),循环结束。6. 当前帧预处理cpp
cvtColor(current_frame, current_gray, COLOR_BGR2GRAY);
GaussianBlur(current_gray, current_gaus, Size(), 10, 0);
对当前帧执行与前两帧相同的预处理步骤:转换为灰度图像(current_gray)并应用高斯模糊(current_gaus)。7. 计算两帧差分cpp
Mat diff1, diff2, diff;subtract(pre_gaus2, pre_gaus1, diff1);
subtract(current_gaus, pre_gaus2, diff2);
计算pre_gaus2与pre_gaus1以及current_gaus与pre_gaus2之间的像素差值,结果分别存储在diff1和diff2。8. 差分图像二值化cpp
Mat diff1_binary, diff2_binary;threshold(diff1, diff1_binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
threshold(diff2, diff2_binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
对diff1和diff2分别应用二值化阈值处理(包括Otsu自适应阈值),得到运动区域的二值图像diff1_binary和diff2_binary。9. 逻辑与操作合并差分结果cpp
bitwise_and(diff1_binary, diff2_binary, diff);
对diff1_binary和diff2_binary进行逻辑与(AND)操作,仅保留两者都为运动区域的像素,生成更稳定的运动检测结果,存储在diff中。10. 形态学开运算cpp
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
morphologyEx(diff, diff, MORPH_OPEN, kernel, Point(-1, -1), 1, 0);
创建一个大小为3x3的矩形结构元素kernel。接着,对diff进行形态学开运算(去除小噪声),输出结果仍保存在diff。11. 显示结果cpp
imshow("diff", diff);
imshow("current_frame", current_frame);
使用imshow函数分别显示运动区域检测结果diff和当前帧原始彩色图像current_frame。12. 更新前两帧信息cpp
pre_gaus1 = pre_gaus2.clone();
pre_gaus2 = current_gaus.clone();
使用clone函数复制pre_gaus2和current_gaus的内容,使得pre_gaus1和pre_gaus2分别存储前两帧高斯模糊后的灰度图像,为下一次循环做好准备。13. 检查用户输入以决定是否退出cpp
char ch = cv::waitKey(20);
if (ch == 27)
{break;
}
waitKey(20)函数等待用户按键,同时设置超时时间为20毫秒。若在该时间内接收到按键事件,返回按键的ASCII码;否则返回-1。这里检查是否按下Esc键(ASCII码为27),如果是,则跳出循环,结束视频处理。总结:这段代码通过双缓冲机制(同时保留两前一帧信息)实现了一种改进的基于两帧差法的运动检测算法。算法流程包括读取帧、预处理、差分计算、二值化、逻辑与操作、形态学开运算等步骤,最终提取出稳定运动区域并在窗口中实时显示,同时允许用户按Esc键随时停止处理。

知道上面步骤我们可以很轻松翻译成opencvsharp代码

【效果展示】

【测试环境】

vs2019,netframework4.7.2,opencvsharp4.8.0

【opencvsharp演示代码下载地址】 

https://download.csdn.net/download/FL1623863129/89085049

相关文章:

[C#]OpenCvSharp使用帧差法或者三帧差法检测移动物体

关于C版本帧差法可以参考博客 [C]OpenCV基于帧差法的运动检测-CSDN博客https://blog.csdn.net/FL1768317420/article/details/137397811?spm1001.2014.3001.5501 我们将参考C版本转成opencvsharp版本。 帧差法,也叫做帧间差分法,这里引用百度百科上的…...

Android Studio学习8——点击事件

在xml代码中绑定 在java代码中绑定 弹出一个toast 随机,数组...

微软detours代码借鉴点备注

comeasy 借鉴点1 Loadlibray的时间选择 注入库wrotei.dll,为了获取istream的接口,需要loadlibrary,但是在dllmain中是不建议这样做的。因此,动态库在dllmain的时候直接挂载了comeasy.exe的入口 //获取入口 TrueEntryPoint (i…...

【c++】类和对象(七)

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 朋友们大家好&#xff0c;本篇文章来到类和对象的最后一部分 目录 1.static成员1.1特性 2.友元2.1引入&#xff1a;<<和>>的重载2.2友元函数2.3友元类 3.内部类4.匿名对象5.拷…...

oracle pdb从12.1迁移到19.20

oracle pdb从12.1迁移到19.20 1 unplug &#xff08;12c的环境执行&#xff09; SQL> alter pluggable database VINCENT_TEST close immediate; SQL> alter pluggable database VINCENT_TEST unplug into /u01/backup/temp_20240401/VINCENT_TEST.xml;2 plug &#xf…...

[Python GUI PyQt] PyQt5快速入门

PyQt5快速入门 PyQt5的快速入门0. 写在前面1. 思维导图2. 第一个PyQt5的应用程序3. PyQt5的常用基本控件和布局3.1 PyQt5的常用基本控件3.1.1 按钮控件 QPushButton3.1.2 文本标签控件 QLabel3.1.3 单行输入框控件 QLineEdit3.1.4 A Quick Widgets Demo 3.2 PyQt5的常用基本控件…...

vue3中播放flv流视频,以及组件封装超全

实现以上功能的播放&#xff0c;只需要传入一个流的地址即可&#xff0c;当然组件也只有简单的实时播放功能 下面直接上组件 里面的flvjs通过npm i flv.js直接下载 <template><div class"player" style"position: relative;"><p style&…...

【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解

&#x1f3e0;专栏介绍&#xff1a;浅尝C专栏是用于记录C语法基础、STL及内存剖析等。 &#x1f3af;每日格言&#xff1a;每日努力一点点&#xff0c;技术变化看得见。 文章目录 继承的概念及定义继承的概念继承的定义定义格式继承关系与访问限定符 基类和派生类对象赋值转换继…...

tomcat中的web项目配置指引

文章目录 目录结构I server.xml 配置文件1.1 Host标签1.2 contex标签1.3 server.xml 的端口配置1.4 appBase和docBase的区别1.5 Engine标签1.6 Connector标签II Tomcat应用的配置2.1 配置虚拟路径2.2 配置连接数2.3 使用线程池2.4 配置内存大小III 预备知识...

如果你正在投简历,一定要试试这款AI工具!

今天给大家分享一款AI简历神器 - BitBitFly AI 简历助手&#xff0c;这个工具可以帮助大家快速、精准投简历&#xff0c;并且提供职位匹配度分析报告&#xff0c;提供专业优化简历建议提高简历和职位匹配度&#xff0c;轻松拿下offer。 如果你在找工作的时候遇到以下问题&…...

Unity:2D SpriteShape

1.1 简介 Sprite Shape 可以很灵活的更改sprite的轮廓。比如&#xff1a; 它由两部分组成&#xff1a;Sprite Shape Profile、Sprite Shape Controller&#xff0c;需要导入2D Sprite Shape Package. 1.1.1 Sprite导入要求 Texture Type - ‘Sprite (2D and UI)’.Sprite Mo…...

Web大并发集群部署之集群介绍

一、传统web访问模型 传统web访问模型完成一次请求的步骤 1&#xff09;用户发起请求 2&#xff09;服务器接受请求 3&#xff09;服务器处理请求&#xff08;压力最大&#xff09; 4&#xff09;服务器响应请求 传统模型缺点 单点故障&#xff1b; 单台服务器资源有限&…...

Linux_进程的优先级环境变量上下文切换

文章目录 一、进程的优先级二、进程的四个重要概念三、上下文切换四、环境变量4.1 查看当前shell环境下的环境变量与内容 一、进程的优先级 什么是优先级&#xff1f; 指定一个进程获取某种资源的先后顺序本质是进程获取cpu资源的优先顺序 为什么要有优先级 进程访问的资源&am…...

【Rust】语言特点介绍

Rust 教程 Rust 语言是一种高效、可靠的通用高级语言。其高效不仅限于开发效率&#xff0c;它的执行效率也是令人称赞的&#xff0c;是一种少有的兼顾开发效率和执行效率的语言。 Rust 语言由 Mozilla 开发&#xff0c;最早发布于 2014 年 9 月。Rust 的编译器是在 MIT Licens…...

接口冒烟测试方法

接口冒烟测试方法 今年遇到了几个问题&#xff0c;与接口的功能和性能相关&#xff0c;恰巧最近公司也在组织以冒烟测试为主题的活动&#xff0c;于是乎突发奇想&#xff0c;寻思着能否将接口测试与冒烟测试结合起来&#xff0c;发掘一些新的接口测试思路与方法。 平时对接口…...

Redis 全景图(3)--- Redis 应用于缓存

前言 这是关于 Redis 全景图的最后一篇文章。因为一次写太多会限流&#xff0c;我也是没办法&#xff0c;才分成三篇文章来写。这篇文章是关于 Redis 应用于缓存的。 其实为什么要讲这个话题呢&#xff1f; Redis 应用在很多地方呀&#xff0c;为什么一定要挑着这个话题来讲呢…...

vue中splice方法总结

本文没有目录&#xff0c;很简单的几句话总结一下 1&#xff0c;参数解释2&#xff0c;使用方法 splice(index,len,item)是vue中对数组进行操作的方法之一&#xff0c;可以用来 删除&#xff0c; 更新&#xff0c;和 增加数组内容。 1&#xff0c;参数解释 index&#xff1a…...

【HTML】CSS样式(二)

上一篇我们学习了CSS基本样式和选择器&#xff0c;相信大家对于样式的使用有了初步认知。 本篇我们继续来学习CSS中的扩展选择器及CSS继承性&#xff0c;如何使用这些扩展选择器更好的帮助我们美化页面。 下一篇我们将会学习CSS中常用的属性。 喜欢的 【点赞】【关注】【收藏】…...

Java 学习和实践笔记(51):二分法查找(折半检索)

二分法查找&#xff08;折半检索&#xff09;又叫binary search. 要在一堆数据中查找是否存在某一个已知数&#xff0c;二分法查找的步骤&#xff1a; 第一步&#xff0c;对数据实现排序 第二步&#xff0c;将该数与排序后的数据集的中间一个数进行比较 第三步&#xff0c;…...

echarts 地图 自己圈地图 乡镇街道

这个是方式是我实在不愿意做的&#xff01; 如果有现成的最好&#xff0c;没有办法的情况下再用这个东西。 今天公司有一个项目&#xff0c;地方划分了一块区域&#xff0c;但是国家没有审核&#xff0c;但是项目里面用到了一个地图展示数据&#xff01;然后就需要我们自己把…...

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇&#xff0c;在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下&#xff1a; 【Note】&#xff1a;如果你已经完成安装等操作&#xff0c;可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作&#xff0c;重…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

什么是Ansible Jinja2

理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具&#xff0c;可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板&#xff0c;允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板&#xff0c;并通…...

C#中的CLR属性、依赖属性与附加属性

CLR属性的主要特征 封装性&#xff1a; 隐藏字段的实现细节 提供对字段的受控访问 访问控制&#xff1a; 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性&#xff1a; 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑&#xff1a; 可以…...

mac 安装homebrew (nvm 及git)

mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用&#xff1a; 方法一&#xff1a;使用 Homebrew 安装 Git&#xff08;推荐&#xff09; 步骤如下&#xff1a;打开终端&#xff08;Terminal.app&#xff09; 1.安装 Homebrew…...