譯者| 朱先忠
審校| 孫淑娟
由於Python程式語言面臨多個開源庫可用;因此,選擇Python語言進行運動偵測開發是很容易的。目前,運動偵測已經有許多的商業應用。例如,它可以用於線上考試的監考,也可以用於商店、銀行等領域的安全目的。
Python程式語言是一種開源的且支援函式庫極其豐富的語言,如今基於此語言已經為用戶開發了大量的應用程序,並擁有大量用戶。正因為如此,Python語言在市場上快速成長。 Python語言的優點不勝枚舉,不僅是因為它的語法簡單、易於發現錯誤,而且其非常迅速的調試過程使其對使用者更加友好。
為什麼建議您學習Python呢?我們可以使用下圖來簡單說明:
Python於1991年設計,由Python軟體基金會開發。如今已經發布了許多版本。其中,Python2和Python3最著名。目前,Python3已經被廣泛使用,用戶數量也在快速成長。在本文這個專案中,我們將使用Python3作為開發語言。
根據物理學原理,當一個物體靜止不動且沒有速度時,它就被認為處於靜止狀態;而恰恰相反的是,當一個物體沒有完全靜止且在某個方向(無論是左右、前後或上下)上有一定的運動或速度時,它都被認為處於運動狀態。在本文中,我們將嘗試偵測出物體的運作情況。
目前,運動偵測在現實生活中已經存在大量實現或應用,這充分證明其巨大的應用價值。其中,使用網路攝影機(我們將在本文中實現)作為安全警衛進行線上考試監考等是最典型的應用。
在本文中,我們將嘗試實作一個腳本。透過該腳本,我們將使用桌上型電腦或筆記型電腦上安裝的網路攝影機實現對物體運動的檢測。我們的想法是,我們將拍攝兩幀視頻,並試圖找出它們之間的差異。如果兩個畫面之間存在某種差異,那麼很明顯,相機前面的物體存在某種移動,這就產生了差異。
在著手實現程式碼之前,讓我們先看看我們將在程式碼中使用的一些模組或函式庫,這些模組或函式庫用於操作網路攝影機進行運動檢測。正如我們所討論的,這些開源程式庫對於Python聲譽的傳播起著重要作用。讓我們看看本文範例專案中所需的開源函式庫:
上述兩個函式庫OpenCV和Pandas都是純粹基於Python的免費開源函式庫,我們將透過Python語言的Python3版本來使用它們。
OpenCV是一個非常著名的開源程式庫,可以與許多程式語言(如C 、Python等)一起使用,專門應用於處理圖像和視訊程式開發。透過與Python的開源函式庫Pandas或NumPy函式庫整合應用,我們可以充分挖掘出OpenCV的功能。
Pandas是一個開放原始碼的Python函式庫,為資料分析提供了豐富的內建工具;因此,在資料科學和資料分析領域得到了廣泛的應用。在Pandas中提供了資料結構形式DataFrame資料幀,這種結構對於將表格資料操作和儲存到二維資料結構中提供了極方便的支援。
上述兩個模組都不是Python內建的,我們必須在使用前先安裝它們。除此之外,我們還將在專案中使用另外兩個模組。
這兩個模組都內建在Python中,以後無需安裝。這些模組分別用來處理與日期和時間相關的功能。
到目前為止,我們已經看到了我們將在程式碼中使用的函式庫。接下來,讓我們從影片只是許多靜態影像或影格的組合這一假定開始,然後使用所有這些影格的組合來建立一個影片。
在本節中,我們會先匯入所有函式庫,如Pandas和OpenCV。然後,我們從DateTime模組匯入time和DateTime函數。
#导入Pandas库 import Pandas as panda # 导入OpenCV库 import cv2 #导入时间模块 import time #从datetime 模块导入datetime 函数 from datetime import datetime
在本節中,我們要初始化一些變量,並將在程式碼中進一步使用這些變數。首先,我們將初始狀態定義為“None”,然後透過另一個變數motionTrackList來儲存追蹤的運動。
此外,我们还定义了一个列表“motionTime”,用于存储发现运动的时间,并使用Panda的模块初始化数据帧列表。
# 对于初始帧,以变量initialState的形式将初始状态指定为None initialState = None # 帧中检测到任何运动时存储所有轨迹的列表 motionTrackList= [ None, None ] # 一个新的“时间”列表,用于存储检测到移动时的时间 motionTime = [] # 使用带有初始列和最终列的Panda库初始化数据帧变量“DataFrame” dataFrame = panda.DataFrame(columns = ["Initial", "Final"])
在本节中,我们将实现本文示例项目中最关键的运动检测步骤。下面,让我们分步骤进行解说:
# 使用cv2模块启动网络摄像头以捕获视频 video = cv2.VideoCapture(0) # 使用无限循环从视频中捕获帧 while True: # 使用read功能从视频中读取每个图像或帧 check, cur_frame = video.read() #将'motion'变量定义为等于零的初始帧 var_motion = 0 # 从彩色图像创建灰色帧 gray_image = cv2.cvtColor(cur_frame, cv2.COLOR_BGR2GRAY) # 从灰度图像中使用GaussianBlur函数找到变化部分 gray_frame = cv2.GaussianBlur(gray_image, (21, 21), 0) # 在第一次迭代时进行条件检查 # 如果为None,则把grayFrame赋值给变量initalState if initialState is None: initialState = gray_frame continue # 计算静态(或初始)帧与我们创建的灰色帧之间的差异 differ_frame = cv2.absdiff(initialState, gray_frame) # 静态或初始背景与当前灰色帧之间的变化将突出显示 thresh_frame = cv2.threshold(differ_frame, 30, 255, cv2.THRESH_BINARY)[1] thresh_frame = cv2.dilate(thresh_frame, None, iterations = 2) #对于帧中的移动对象,查找轮廓 cont,_ = cv2.findContours(thresh_frame.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for cur in cont: if cv2.contourArea(cur) < 10000: continue var_motion = 1 (cur_x, cur_y,cur_w, cur_h) = cv2.boundingRect(cur) # 在移动对象周围创建一个绿色矩形 cv2.rectangle(cur_frame, (cur_x, cur_y), (cur_x + cur_w, cur_y + cur_h), (0, 255, 0), 3) # 从帧中添加运动状态 motionTrackList.append(var_motion) motionTrackList = motionTrackList[-2:] # 添加运动的开始时间 if motionTrackList[-1] == 1 and motionTrackList[-2] == 0: motionTime.append(datetime.now()) # 添加运动的结束时间 if motionTrackList[-1] == 0 and motionTrackList[-2] == 1: motionTime.append(datetime.now()) # 在显示捕获图像的灰度级中 cv2.imshow("The image captured in the Gray Frame is shown below: ", gray_frame) # 显示初始静态帧和当前帧之间的差异 cv2.imshow("Difference between theinital static frame and the current frame: ", differ_frame) # 在框架屏幕上显示视频中的黑白图像 cv2.imshow("Threshold Frame created from the PC or Laptop Webcam is: ", thresh_frame) #通过彩色框显示物体的轮廓 cv2.imshow("From the PC or Laptop webcam, this is one example of the Colour Frame:", cur_frame) # 创建处于等待状态的键盘按键 wait_key = cv2.waitKey(1) # 按下'm'键时,结束整个进程执行 if wait_key == ord('m'): # 当屏幕上有东西移动时,将运动变量值添加到motiontime列表中 if var_motion == 1: motionTime.append(datetime.now()) break
在结束循环体执行后,我们将从dataFrame和motionTime列表中添加数据到CSV文件中,最后关闭视频。这一部分代码的实现如下所示:
# 最后,我们在数据帧中添加运动时间 for a in range(0, len(motionTime), 2): dataFrame = dataFrame.append({"Initial" : time[a], "Final" : motionTime[a + 1]}, ignore_index = True) # 创建CSV文件记录下所有运动信息 dataFrame.to_csv("EachMovement.csv") # 释放视频内存 video.release() #现在,在openCV的帮助下关闭或销毁所有打开的窗口 cv2.destroyAllWindows()
至此,我们已经成功地创建完所有代码。现在,让我们再次归纳一下整个过程。
首先,我们使用设备的网络摄像头捕捉视频,然后将输入视频的初始帧作为参考,并不时检查下一帧。如果发现与第一帧不同的帧,则说明存在运动。该信息将被标记在绿色矩形中。
现在,让我们把上面所有代码片断连接到一起,如下所示:
#导入Pandas库 import Pandas as panda # 导入OpenCV库 import cv2 #导入时间模块 import time #从datetime 模块导入datetime 函数 from datetime import datetime # 对于初始帧,以变量initialState的形式将初始状态指定为None initialState = None # 帧中检测到任何运动时存储所有轨迹的列表 motionTrackList= [ None, None ] # 一个新的“时间”列表,用于存储检测到移动时的时间 motionTime = [] # 使用带有初始列和最终列的Panda库初始化数据帧变量“DataFrame” dataFrame = panda.DataFrame(columns = ["Initial", "Final"]) # 使用cv2模块启动网络摄像头以捕获视频 video = cv2.VideoCapture(0) # 使用无限循环从视频中捕获帧 while True: # 使用read功能从视频中读取每个图像或帧 check, cur_frame = video.read() #将'motion'变量定义为等于零的初始帧 var_motion = 0 # 从彩色图像创建灰色帧 gray_image = cv2.cvtColor(cur_frame, cv2.COLOR_BGR2GRAY) # 从灰度图像中使用GaussianBlur函数找到变化部分 gray_frame = cv2.GaussianBlur(gray_image, (21, 21), 0) # 在第一次迭代时进行条件检查 # 如果为None,则把grayFrame赋值给变量initalState if initialState is None: initialState = gray_frame continue # 计算静态(或初始)帧与我们创建的灰色帧之间的差异 differ_frame = cv2.absdiff(initialState, gray_frame) # 静态或初始背景与当前灰色帧之间的变化将突出显示 thresh_frame = cv2.threshold(differ_frame, 30, 255, cv2.THRESH_BINARY)[1] thresh_frame = cv2.dilate(thresh_frame, None, iterations = 2) #对于帧中的移动对象,查找轮廓 cont,_ = cv2.findContours(thresh_frame.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for cur in cont: if cv2.contourArea(cur) < 10000: continue var_motion = 1 (cur_x, cur_y,cur_w, cur_h) = cv2.boundingRect(cur) # 在移动对象周围创建一个绿色矩形 cv2.rectangle(cur_frame, (cur_x, cur_y), (cur_x + cur_w, cur_y + cur_h), (0, 255, 0), 3) # 从帧中添加运动状态 motionTrackList.append(var_motion) motionTrackList = motionTrackList[-2:] # 添加运动的开始时间 if motionTrackList[-1] == 1 and motionTrackList[-2] == 0: motionTime.append(datetime.now()) # 添加运动的结束时间 if motionTrackList[-1] == 0 and motionTrackList[-2] == 1: motionTime.append(datetime.now()) # 在显示捕获图像的灰度级中 cv2.imshow("The image captured in the Gray Frame is shown below: ", gray_frame) # 显示初始静态帧和当前帧之间的差异 cv2.imshow("Difference between theinital static frame and the current frame: ", differ_frame) # 在框架屏幕上显示视频中的黑白图像 cv2.imshow("Threshold Frame created from the PC or Laptop Webcam is: ", thresh_frame) #通过彩色框显示物体的轮廓 cv2.imshow("From the PC or Laptop webcam, this is one example of the Colour Frame:", cur_frame) # 创建处于等待状态的键盘按键 wait_key = cv2.waitKey(1) # 借助'm'键结束我们系统的整个进行 if wait_key == ord('m'): # 当屏幕上有物体运行时把运动变量值添加到列表motiontime中 if var_motion == 1: motionTime.append(datetime.now()) break # 最后,我们在数据帧中添加运动时间 for a in range(0, len(motionTime), 2): dataFrame = dataFrame.append({"Initial" : time[a], "Final" : motionTime[a + 1]}, ignore_index = True) # 记录下所有运行,并创建到一个CSV文件中 dataFrame.to_csv("EachMovement.csv") # 释放视频内存 video.release() #现在,在openCV的帮助下关闭或销毁所有打开的窗口 cv2.destroyAllWindows()
运行上述代码后得到的结果与下面看到的结果类似。
从这个动画中,我们可以看到该男子在视频中的动作已经被跟踪。因此,可以相应地看到输出结果。
然而,在这段代码中,跟踪是在移动对象周围的矩形框的帮助下完成,类似于下面动画中所示的。这里要注意的一件有趣的事情是,这段视频是一个实际的安全摄像头的镜头,已经对其进行了检测处理。
最后来归纳一下。本文中所介绍的运动检测的主要思想是,每个视频只是许多静态图像(称为帧)的组合,我们是通过判断帧之间的差异来实现运行检测的。
朱先忠,51CTO社区编辑,51CTO专家博客、讲师,潍坊一所高校计算机教师,自由编程界老兵一枚。早期专注各种微软技术(编著成ASP.NET AJX、Cocos 2d-X相关三本技术图书),近十多年投身于开源世界(熟悉流行全栈Web开发技术),了解基于OneNet/AliOS+Arduino/ESP32/树莓派等物联网开发技术与Scala+Hadoop+Spark+Flink等大数据开发技术。
原文标题:How to Perform Motion Detection Using Python,作者:Vaishnavi Amira Yada
以上是Python運動偵測程式實戰演練的詳細內容。更多資訊請關注PHP中文網其他相關文章!