
这个该死的家伙。我就知道他偷了我最后一罐啤酒!
对于一个男人来讲,这些话永远都不该说。但是当我关上冰箱门的时候,我愤怒地叹息,感到厌恶,自言自语地说了这些。
你看,我花了12个小时写了这篇将要发表的文章《PyImageSearch Gurus course》。我的脑子都糊掉了,像个半熟的摊鸡蛋一样,几乎要从耳朵里流出来了。当我深夜决定结束工作的时候,我只想放松一下,看看我最爱的电影——《侏罗纪公园》。同时喝着来自 Smuttynose 的最好的 IPA 冰啤,Smuttynose 是近来我非常喜欢的一家酒厂。
但是,昨天晚上来串门的该死的 James 喝掉了我最后一罐啤酒。
好吧,据称。
我并不能证明任何我的猜测。实际上,我并没有亲眼看到他喝我的啤酒,因为我埋头于笔记本电脑中,手指在键盘上跳动,兴奋地敲击出教程和文章。但是我感觉他就是嫌疑犯。他是我唯一会喝 IPA 的(前)朋友。
所以我做了一件任何男人都会做的事。
我在橱柜顶上安装了一个树莓派,来探测看他是不是打算再次偷啤酒。

过分了?
也许吧。
但是,我很看重我的啤酒。而且如果 James 再次尝试偷我的啤酒的话,我会逮他个正着。
一篇关于运动检测的系列文章(分为两部分)
做一个用于家庭监控的运动检测和追踪系统,分两部分,本文是第一篇。
本文接下来的部分,将会详细介绍如何使用计算机视觉技术来建立一个用于家庭监控的基础的运动检测和追踪系统。本例对预先录制的视频和网络摄像头的实时数据流都可以工作;然而,我们将会在我们的笔记本/桌面电脑上进行开发。
在本系列的第二部分中,我会向你展示如何升级代码,使其可以在树莓派和camera board上工作,以及如何扩展家庭监控系统,来捕捉任何检测到的运动,并且上传到你的个人Dropbox中。
也许到了最后,我们可以把 James 抓个正着。
背景移除是很多计算机视觉应用的关键内容。我们通过它来计算经过收费站的汽车个数。我们通过它来计算进进出出一间商店的人的个数。
同时我们使用它来进行运动检测。
在本文开始写代码之前,让我告诉你,OpenCV 里有很多很多方法来进行运动检测、追踪和分析。有一些非常简单,而另外一些非常复杂。两个初级的方法是某种形式的基于混合高斯模型的前景和背景分割:
cv2.BackgroundSubtractorMOG来使用。cv2.BackgroundSubtractorMOG2 来使用。在新版本的 OpenCV 中,我们有基于贝叶斯(概率)的前景和背景分割,是 Godbehere 等人在2012年的文章中实现的,《Visual Tracking of Human Visitors under Variable-Lighting Conditions for a Responsive Audio Art Installation》,我们可以在cv2.createBackgroundSubtractorGMG 中找到它的实现(然而我们需要等OpenCV 3的到来,才能使用它的全部功能。)
所有这些方法都涉及到从前景中分离背景(它们甚至提供相应的机制来让我们辨别实际运动和阴影及关照的细微改变)!
为什么这一点特别重要?为什么我们这么在意哪个像素属于前景哪个像素属于背景?
在运动检测中,我们会做出如下的假设:
我们视频流中的背景在连续的视频帧内,多数时候应该是静止不变的,因此如果我们可以建立背景模型,我们的就可以监视到显著的变化。如果发生了显著的变化,我们就可以检测到它——通常这些变化和我们视频中的运动有关。
显然在现实世界中,我们这个假设比较容易失效。因为阴影、反色、光照条件以及环境中可能发生的其他变化,我们的背景可能会看上去变得非常不同,这会让我们的算法失效。所以为什么最成功的背景移除/前景检测系统需要固定安装的相机以及控制光照条件。
上面我提到的方法,尽管非常强大,但同时计算非常耗时。而且我们最终的目标是在本系列的最后,把该系统部署在树莓派上,因此我们最好可以坚持使用简单的方法。我们将在未来的文章中回到这些强大的方法上,但是目前我们将保持简单和高效。
好了,准备好帮助我开发一个家用监视系统来抓住那个偷啤酒的混蛋了么? 打开编辑器,新建一个文件,命名为 motion_detector.py,然后让我们开始写代码吧。
| 123456789101112131415161718192021222324 | # 导入必要的软件包import argparseimport datetimeimport imutilsimport timeimport cv2 # 创建参数解析器并解析参数ap = argparse.ArgumentParser()ap.add_argument(\”-v\”, \”–video\”, help=\”path to the video file\”)ap.add_argument(\”-a\”, \”–min-area\”, type=int, default=500, help=\”minimum area size\”)args = vars(ap.parse_args()) # 如果video参数为None,那么我们从摄像头读取数据if args.get(\”video\”, None) is None: camera = cv2.VideoCapture(0) time.sleep(0.25) # 否则我们读取一个视频文件else: camera = cv2.VideoCapture(args[\”video\”]) # 初始化视频流的第一帧firstFrame = None |
2-6行导入了我们必要的软件包。这些看上去都很熟悉,除了imutils这个包,它提供了一组由我编写的非常方便的函数,来让我们更简单的进行图像处理。如果你还没有安装 imutils 到你的系统,你可以通过pip来安装:pip install imutils
下一步,我们在9-12行解析了命令行参数。我们定义了两个选项。第一个,--video,是可选的。它会指定一个路径,指向一个预先录制好的视频文件,我们可以检测该视频中的运动。如果你不提供视频的路径,那么OpenCV会从你的摄像头中来检测运动。
我们同时还定义了--min-area,它表示一个图像区域被看做实际运动的最小尺寸(以像素为单位)。正如我接下来要讲的那样,我们会发现图像中比较小的区域变化会比较显著,可能是因为噪点或是光线的变化。在实际中,这些小区域并不是实际的运动——所以我们定义一个最小的尺寸来对付和过滤掉这些假阳性(false-positives)结果。
15-21行获取一个我们摄像机对象的引用。在这个例子中,没有提供视频路径(15-17行),我们会取得一个摄像头的引用。如果提供了一个视频文件路径,那么我们会在20-21行建立一个指向它的指针。
最后,我们以一个变量来结束这段代码,这个变量是firstFrame。 能猜到firstFrame 是什么吗?
假设:视频的第一帧不会包含运动,而仅仅是背景——因此我们可以使用第一帧来建立背景模型。 显然我们此处建立的假设有些太大了。但是再说一次,我们的目标是要在树莓派上运行这个系统,所以我们不能做的太复杂。正如你会在本文的结果一节所看到的那样,当有人在屋里走动的时候,我们可以轻易的检测到运动并追踪他们。
| 12345678910111213141516171819 | # 遍历视频的每一帧while True: # 获取当前帧并初始化occupied/unoccupied文本 (grabbed, frame) = camera.read() text = \”Unoccupied\” # 如果不能抓取到一帧,说明我们到了视频的结尾 if not grabbed: break # 调整该帧的大小,转换为灰阶图像并且对其进行高斯模糊 frame = imutils.resize(frame, width=500) gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) gray = cv2.GaussianBlur(gray, (21, 21), 0) # 如果第一帧是None,对其进行初始化 if firstFrame is None: firstFrame = gray continue |
现在我们已经获取了视频文件/摄像头数据流的引用,我们可以在第一行(原文第27行)开始遍历每一帧了。
调用camera.read()为我们返回一个2元组。元组的第一个值是grabbed,表明是否成功从缓冲中读取了frame。元组的第二个值就是frame它本身。
我们同时还定义了一个叫做 text 的字符串,并对其进行初始化来表明我们正在监控的这个房间“没有被占领”(Unoccupied)。如果这个房间确实有活动,我们可以更新这个字符串。
在这个例子中,如果没有成功从视频文件中读取一帧,我们会在10-11行(原文35-36行)跳出循环。
我们可以开始处理帧数据并准备进行运动分析(15-17行)。我们首先会调整它的大小到500像素宽——没有必要去直接处理视频流中的大尺寸,原始图像。我们同样会把图片转换为灰阶图像,因为彩色数据对我们的运动检测算法没有影响。最后,我们会使用高斯模糊来平滑我们的图像。
认识到即使是相邻帧,也不是完全相同的这一点很重要!
由于数码相机传感器的微小变化,没有100%相同的两帧数据——一些像素肯定会有不同的强度值。也就是说,我们需要,并应用高斯平滑对一个11X11的区域的像素强度进行平均。这能帮我们滤除可能使我们运动检测算法失效的高频噪音。
正如我在上面提到的,我们需要通过某种方式对我们的图像进行背景建模。再一次的,我们会假设视频的第一帧不包含任何运动,它是一个很好的例子,表明我们的背景是如何的。如果firstFrame没有初始化,我们会把它保存然后继续处理视频的下一帧。(20-22行)
这里有一个关于示例视频第一帧的例子:
上面这一帧满足我们的假设,视频的第一帧仅仅是一个静止的背景——没有运动。
有了这个静止的背景图片,我们已经准备好实时运动检测和追踪了:
| 123456789101112131415161718192021 | # 计算当前帧和第一帧的不同 frameDelta = cv2.absdiff(firstFrame, gray) thresh = cv2.rame, gray) thresh = cv2./trans-team/\”>翻译组。
这个该死的家伙。我就知道他偷了我最后一罐啤酒! 对于一个男人来讲,这些话永远都不该说。但是当我关上冰箱门的时候,我愤怒地叹息,感到厌恶,自言自语地说了这些。 你看,我花了12个小时写了这篇将要发表的文章《PyImageSearch Gurus course》。我的脑子都糊掉了,像个半熟的摊鸡蛋一样,几乎要从耳朵里流出来了。当我深夜决定结束工作的时候,我只想放松一下,看看我最爱的电影——《侏罗纪公园》。同时喝着来自 Smuttynose 的最好的 IPA 冰啤,Smuttynose 是近来我非常喜欢的一家酒厂。 但是,昨天晚上来串门的该死的 James 喝掉了我最后一罐啤酒。 好吧,据称。 我并不能证明任何我的猜测。实际上,我并没有亲眼看到他喝我的啤酒,因为我埋头于笔记本电脑中,手指在键盘上跳动,兴奋地敲击出教程和文章。但是我感觉他就是嫌疑犯。他是我唯一会喝 IPA 的(前)朋友。 所以我做了一件任何男人都会做的事。 我在橱柜顶上安装了一个树莓派,来探测看他是不是打算再次偷啤酒。
过分了? 也许吧。 但是,我很看重我的啤酒。而且如果 James 再次尝试偷我的啤酒的话,我会逮他个正着。 一篇关于运动检测的系列文章(分为两部分) 做一个用于家庭监控的运动检测和追踪系统,分两部分,本文是第一篇。 本文接下来的部分,将会详细介绍如何使用计算机视觉技术来建立一个用于家庭监控的基础的运动检测和追踪系统。本例对预先录制的视频和网络摄像头的实时数据流都可以工作;然而,我们将会在我们的笔记本/桌面电脑上进行开发。 在本系列的第二部分中,我会向你展示如何升级代码,使其可以在树莓派和camera board上工作,以及如何扩展家庭监控系统,来捕捉任何检测到的运动,并且上传到你的个人Dropbox中。 也许到了最后,我们可以把 James 抓个正着。 一点关于背景移除的内容背景移除是很多计算机视觉应用的关键内容。我们通过它来计算经过收费站的汽车个数。我们通过它来计算进进出出一间商店的人的个数。 同时我们使用它来进行运动检测。 在本文开始写代码之前,让我告诉你,OpenCV 里有很多很多方法来进行运动检测、追踪和分析。有一些非常简单,而另外一些非常复杂。两个初级的方法是某种形式的基于混合高斯模型的前景和背景分割:
在新版本的 OpenCV 中,我们有基于贝叶斯(概率)的前景和背景分割,是 Godbehere 等人在2012年的文章中实现的,《Visual Tracking of Human Visitors under Variable-Lighting Conditions for a Responsive Audio Art Installation》,我们可以在 所有这些方法都涉及到从前景中分离背景(它们甚至提供相应的机制来让我们辨别实际运动和阴影及关照的细微改变)! 为什么这一点特别重要?为什么我们这么在意哪个像素属于前景哪个像素属于背景? 在运动检测中,我们会做出如下的假设: 我们视频流中的背景在连续的视频帧内,多数时候应该是静止不变的,因此如果我们可以建立背景模型,我们的就可以监视到显著的变化。如果发生了显著的变化,我们就可以检测到它——通常这些变化和我们视频中的运动有关。 显然在现实世界中,我们这个假设比较容易失效。因为阴影、反色、光照条件以及环境中可能发生的其他变化,我们的背景可能会看上去变得非常不同,这会让我们的算法失效。所以为什么最成功的背景移除/前景检测系统需要固定安装的相机以及控制光照条件。 上面我提到的方法,尽管非常强大,但同时计算非常耗时。而且我们最终的目标是在本系列的最后,把该系统部署在树莓派上,因此我们最好可以坚持使用简单的方法。我们将在未来的文章中回到这些强大的方法上,但是目前我们将保持简单和高效。 用 Python 和 OpenCV 进行基础的运动检测和追踪好了,准备好帮助我开发一个家用监视系统来抓住那个偷啤酒的混蛋了么? 打开编辑器,新建一个文件,命名为 motion_detector.py,然后让我们开始写代码吧。
2-6行导入了我们必要的软件包。这些看上去都很熟悉,除了 下一步,我们在9-12行解析了命令行参数。我们定义了两个选项。第一个, 我们同时还定义了 15-21行获取一个我们摄像机对象的引用。在这个例子中,没有提供视频路径(15-17行),我们会取得一个摄像头的引用。如果提供了一个视频文件路径,那么我们会在20-21行建立一个指向它的指针。 最后,我们以一个变量来结束这段代码,这个变量是 假设:视频的第一帧不会包含运动,而仅仅是背景——因此我们可以使用第一帧来建立背景模型。 显然我们此处建立的假设有些太大了。但是再说一次,我们的目标是要在树莓派上运行这个系统,所以我们不能做的太复杂。正如你会在本文的结果一节所看到的那样,当有人在屋里走动的时候,我们可以轻易的检测到运动并追踪他们。
现在我们已经获取了视频文件/摄像头数据流的引用,我们可以在第一行(原文第27行)开始遍历每一帧了。 调用 我们同时还定义了一个叫做 在这个例子中,如果没有成功从视频文件中读取一帧,我们会在10-11行(原文35-36行)跳出循环。 我们可以开始处理帧数据并准备进行运动分析(15-17行)。我们首先会调整它的大小到500像素宽——没有必要去直接处理视频流中的大尺寸,原始图像。我们同样会把图片转换为灰阶图像,因为彩色数据对我们的运动检测算法没有影响。最后,我们会使用高斯模糊来平滑我们的图像。 认识到即使是相邻帧,也不是完全相同的这一点很重要! 由于数码相机传感器的微小变化,没有100%相同的两帧数据——一些像素肯定会有不同的强度值。也就是说,我们需要,并应用高斯平滑对一个11X11的区域的像素强度进行平均。这能帮我们滤除可能使我们运动检测算法失效的高频噪音。 正如我在上面提到的,我们需要通过某种方式对我们的图像进行背景建模。再一次的,我们会假设视频的第一帧不包含任何运动,它是一个很好的例子,表明我们的背景是如何的。如果 这里有一个关于示例视频第一帧的例子: 上面这一帧满足我们的假设,视频的第一帧仅仅是一个静止的背景——没有运动。 有了这个静止的背景图片,我们已经准备好实时运动检测和追踪了:
|