1. 程式人生 > >用 Python 和 OpenCV 檢測和跟蹤運動物件

用 Python 和 OpenCV 檢測和跟蹤運動物件

這個該死的傢伙。我就知道他偷了我最後一罐啤酒!

對於一個男人來講,這些話永遠都不該說。但是當我關上冰箱門的時候,我憤怒地嘆息,感到厭惡,自言自語地說了這些。

你看,我花了12個小時寫了這篇將要發表的文章《PyImageSearch Gurus course》。我的腦子都糊掉了,像個半熟的攤雞蛋一樣,幾乎要從耳朵裡流出來了。當我深夜決定結束工作的時候,我只想放鬆一下,看看我最愛的電影——《侏羅紀公園》。同時喝著來自 Smuttynose 的最好的 IPA 冰啤,Smuttynose 是近來我非常喜歡的一家酒廠。

但是,昨天晚上來串門的該死的 James 喝掉了我最後一罐啤酒。

好吧,據稱。

我並不能證明任何我的猜測

。實際上,我並沒有親眼看到他喝我的啤酒,因為我埋頭於膝上型電腦中,手指在鍵盤上跳動,興奮地敲擊出教程和文章。但是我感覺他就是嫌疑犯。他是我唯一會喝 IPA 的(前)朋友。

所以我做了一件任何男人都會做的事。

我在櫥櫃頂上安裝了一個樹莓派,來探測看他是不是打算再次偷啤酒。

過分了?

也許吧。

但是,我很看重我的啤酒。而且如果 James 再次嘗試偷我的啤酒的話,我會逮他個正著。

一篇關於運動檢測的系列文章(分為兩部分)

做一個用於家庭監控的運動檢測和追蹤系統,分兩部分,本文是第一篇。

本文接下來的部分,將會詳細介紹如何使用計算機視覺技術來建立一個用於家庭監控的基礎的運動檢測和追蹤系統。本例對預先錄製的視訊和網路攝像頭的實時資料流都可以工作;然而,我們將會在我們的筆記本/桌面電腦上進行開發。

在本系列的第二部分中,我會向你展示如何升級程式碼,使其可以在樹莓派和camera board上工作,以及如何擴充套件家庭監控系統,來捕捉任何檢測到的運動,並且上傳到你的個人Dropbox中。

也許到了最後,我們可以把 James 抓個正著。

一點關於背景移除的內容

背景移除是很多計算機視覺應用的關鍵內容。我們通過它來計算經過收費站的汽車個數。我們通過它來計算進進出出一間商店的人的個數。

同時我們使用它來進行運動檢測。

在本文開始寫程式碼之前,讓我告訴你,OpenCV 裡有很多很多方法來進行運動檢測、追蹤和分析。有一些非常簡單,而另外一些非常複雜。兩個初級的方法是某種形式的基於混合高斯模型的前景和背景分割:

所有這些方法都涉及到從前景中分離背景(它們甚至提供相應的機制來讓我們辨別實際運動和陰影及關照的細微改變)!

為什麼這一點特別重要?為什麼我們這麼在意哪個畫素屬於前景哪個畫素屬於背景?

在運動檢測中,我們會做出如下的假設:

我們視訊流中的背景在連續的視訊幀內,多數時候應該是靜止不變的,因此如果我們可以建立背景模型,我們的就可以監視到顯著的變化。如果發生了顯著的變化,我們就可以檢測到它——通常這些變化和我們視訊中的運動有關。

顯然在現實世界中,我們這個假設比較容易失效。因為陰影、反色、光照條件以及環境中可能發生的其他變化,我們的背景可能會看上去變得非常不同,這會讓我們的演算法失效。所以為什麼最成功的背景移除/前景檢測系統需要固定安裝的相機以及控制光照條件。

上面我提到的方法,儘管非常強大,但同時計算非常耗時。而且我們最終的目標是在本系列的最後,把該系統部署在樹莓派上,因此我們最好可以堅持使用簡單的方法。我們將在未來的文章中回到這些強大的方法上,但是目前我們將保持簡單和高效。

用 Python 和 OpenCV 進行基礎的運動檢測和追蹤

好了,準備好幫助我開發一個家用監視系統來抓住那個偷啤酒的混蛋了麼? 開啟編輯器,新建一個檔案,命名為 motion_detector.py,然後讓我們開始寫程式碼吧。

Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 # 匯入必要的軟體包 importargparse importdatetime importimutils importtime importcv2 # 建立引數解析器並解析引數 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,那麼我們從攝像頭讀取資料 ifargs.get("video",None)isNone: 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 是什麼嗎?

假設視訊的第一幀不會包含運動,而僅僅是背景——因此我們可以使用第一幀來建立背景模型。 顯然我們此處建立的假設有些太大了。但是再說一次,我們的目標是要在樹莓派上執行這個系統,所以我們不能做的太複雜。正如你會在本文的結果一節所看到的那樣,當有人在屋裡走動的時候,我們可以輕易的檢測到運動並追蹤他們。

Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 # 遍歷視訊的每一幀 whileTrue: # 獲取當前幀並初始化occupied/unoccupied文字 (grabbed,frame)=camera.read() text="Unoccupied" # 如果不能抓取到一幀,說明我們到了視訊的結尾 ifnotgrabbed: break # 調整該幀的大小,轉換為灰階影象並且對其進行高斯模糊 frame=imutils.resize(frame,width=500) gray=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY) gray=cv2.GaussianBlur(gray,(21,21),0) # 如果第一幀是None,對其進行初始化 iffirstFrame isNone: firstFrame=gray continue

現在我們已經獲取了視訊檔案/攝像頭資料流的引用,我們可以在第一行(原文第27行)開始遍歷每一幀了。

呼叫camera.read()為我們返回一個2元組。元組的第一個值是grabbed,表明是否成功從緩衝中讀取了frame。元組的第二個值就是frame它本身。

我們同時還定義了一個叫做 text 的字串,並對其進行初始化來表明我們正在監控的這個房間“沒有被佔領”(Unoccupied)。如果這個房間確實有活動,我們可以更新這個字串。

在這個例子中,如果沒有成功從視訊檔案中讀取一幀,我們會在10-11行(原文35-36行)跳出迴圈。

我們可以開始處理幀資料並準備進行運動分析(15-17行)。我們首先會調整它的大小到500畫素寬——沒有必要去直接處理視訊流中的大尺寸,原始影象。我們同樣會把圖片轉換為灰階影象,因為彩色資料對我們的運動檢測演算法沒有影響。最後,我們會使用高斯模糊來平滑我們的影象。

認識到即使是相鄰幀,也不是完全相同的這一點很重要!

由於數碼相機感測器的微小變化,沒有100%相同的兩幀資料——一些畫素肯定會有不同的強度值。也就是說,我們需要,並應用高斯平滑對一個11X11的區域的畫素強度進行平均。這能幫我們濾除可能使我們運動檢測演算法失效的高頻噪音。

正如我在上面提到的,我們需要通過某種方式對我們的影象進行背景建模。再一次的,我們會假設視訊的第一幀不包含任何運動,它是一個很好的例子,表明我們的背景是如何的。如果firstFrame沒有初始化,我們會把它儲存然後繼續處理視訊的下一幀。(20-22行

這裡有一個關於示例視訊第一幀的例子:

上面這一幀滿足我們的假設,視訊的第一幀僅僅是一個靜止的背景——沒有運動。

有了這個靜止的背景圖片,我們已經準備好實時運動檢測和追蹤了:

Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # 計算當前幀和第一幀的不同 frameDelta=cv2.absdiff(firstFrame,gray) thresh=cv2.threshold(frameDelta,25,255,cv2.THRESH_BINARY)[1] # 擴充套件閥值影象填充孔洞,然後找到閥值影象上的輪廓 thresh=cv2.dilate(thresh,None,iterations=2) (cnts,_)=cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 遍歷輪廓 forcincnts: # if the contour is too small, ignore it ifcv2.contourArea(c)<args["min_area"]: continue # compute the bounding box for the contour, draw it on the frame, # and update the text # 計算輪廓的邊界框,在當前幀中畫出該框 (x,y,w,h)=cv2.boundingRect(c) cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2) text="Occupied"

現在我們已經從firstFrame變數對背景進行了建模,我們可以利用它來計算起始幀和視訊流資料中後續新幀之間的不同。

計算兩幀的不同是一個簡單的減法,我們使用兩方相應的畫素強度差的絕對值。(第二行

delta = |background_model – current_frame|

兩幀差值圖例如下:

注意到圖片的背景是如何變為黑色的。然而,包含運動的區域(比如包含我自己走過房間動作的區域)會更亮一些。這以為這兩幀差值大的地方是圖片中發生移動的區域。

我們隨後在第3行frameDelta進行閥值化來顯示圖片中畫素強度值有顯著變化的區域。如果差值小於25,我丟棄該畫素將其設定為黑色(例如,背景)。如果差值大於25,我們將其設定為白色(例如,前景)。閥值化的差值圖片如下:

再一次,注意到圖片的背景是黑色的,而前景(運動發生的位置)是白色的。 有了這個閥值化的圖片,只要簡單的進行實施輪廓檢測來找到白色區域的外輪廓線(第7行

我們在第14行開始對輪廓線進行遍歷,在15行濾掉小的,不相關的輪廓。 如果輪廓面積比我們提供的--min-area值大,我們會在前景和移動區域畫邊框線。(23-25行)。我們同樣會更新text狀態字串來表示這個房間”被佔領“(Occupied)了

Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # draw the text and timestamp on the frame # 在當前幀上寫文字以及時間戳 cv2.putText(frame,"Room Status: {}".format(text),(10,20), cv2.FONT_HERSHEY_SIMPLEX,0.5,(0,0,255),2) cv2.putText(frame,datetime.datetime.now().strftime("%A %d %B %Y %I:%M:%S%p"), (10,frame.shape[0]-10),cv2.FONT_HERSHEY_SIMPLEX,0.35,(0,0,255),1) 顯示當前幀並記錄使用者是否按下按鍵 cv2.imshow("Security Feed",frame) cv2.imshow("Thresh",thresh) cv2.imshow("Frame Delta",frameDelta) key=cv2.waitKey(1)&amp;0xFF # 如果q鍵被按下,跳出迴圈 ifkey==ord("q"): break # 清理攝像機資源並關閉開啟的視窗 camera.release() cv2.destroyAllWindows()

11-13行顯示了我的工作成果,執行我們可以在視訊中看到是否檢測到了運動,使用幀差值和閥值影象我們可以除錯我們的指令碼。

注意:如果你下載了本文的原始碼並打算應用到你自己的視訊檔案上,你可能需要改變cv2.threshold 的值和--min-area 引數來獲得你所在光照環境下的最佳效果。

最後,22行和23行清理並釋放了視訊流的指標。

結果

顯然,我要確定我們的運動監測系統可以在James那個偷酒賊再次造訪的之前能夠正常工作——我們將在本系列第二篇文章中談到他。為了測試我們使用Python和OpenCV搭建的運動監測系統,我錄製了兩個視訊檔案。

第一個檔案是example_01.mp4 ,監視了我公寓的正門,當門被開啟時完成檢測。第二個檔案是example_02.mp4 使用安裝在櫥櫃上的樹莓派錄製的。它監控廚房和客廳,當有人在其中走動的時候完成檢測。

讓我們給我們簡單的探測器一次嘗試的機會,開啟終端並執行下面指令:

Python
1 $python motion_detector.py--video videos/example_01.mp4

下圖是一個 gif 圖,顯示來自探測器的一些靜止幀資料。

注意到在門被開啟前沒有進行運動檢測——然後我們可以檢測到我自己從門中走過。你可以在這裡看到全部視訊:

現在,我安裝在用於監視廚房和客廳的攝像機表現如何呢?然我們一探究竟。輸入下面命令:

Python
1 $python motion_detector.py--video videos/example_02.mp4

來自第二個視訊檔案的結果樣本如下:

同樣,這裡是我們運動檢測結果的完整視訊:

正如你看到的,我們的運動檢測系統儘管非常簡單,但表現還不錯!我們可以正常檢測到我進入客廳和離開房間。

然而,現實來講,結果還遠遠談不上完美。儘管只有一個人在屋內走動,我們卻得到了多個外框——這和理想狀態相差甚遠。而且我可以看到,微小的光線變化,比如陰影和牆面反射,都觸發了假陽性的運動檢測結果。

為了解決這些問題,我們依靠OpenCV中更加強大的背景移除方法,這些方法對陰影和少量的反射進行了處理。(我將在未來的文章中談到這些更為先進的背景移除/前景檢測方法)

但是於此同時,請考慮一下我們的最終目標

這個系統,儘管是在我們的筆記本/桌上型電腦系統上開發的,卻是為了要部署在樹莓派上,樹莓派的計算資源非常有限。因此,我們需要讓我們的運動檢測方法保持簡單和快速。我們的運動檢測系統並不完美,很不幸這是一個不利的方面,但是對於我們特定的專案,它仍然能夠很好的完成工作。

最後,如果你想要利用你的攝像頭的原始視訊流來進行運動檢測,空著--video選項即可。

Python
1 $python motion_detector.py

小結

通過本文,我們已經認識到我的朋友James是一個偷酒賊。真是個混蛋啊!

為了能抓他個人贓並獲,我們決定使用Python和OpenCV建立一個運動檢測和追蹤系統。這個系統可以獲取視訊流並分析它們獲取運動。考慮到我們所使用的方法,能夠得到可以接受的監測結果。

最終目標是要把本系統部署在樹莓派上,因此我們沒有依賴OpenCV中一些比較先進的背景移除方法。相反,我們依賴一個簡單,但合理高效的假設——視訊的第一幀僅僅包含我們想要建模的背景,而不包括其他任何東西。

在這個假設下,我們可以實施背景移除,檢測圖片中的運動,在檢測到運動的區域畫出輪廓框。

在這個關於運動檢測系列文章的第二部分,我們會更新程式碼使其在樹莓派上執行

我們同樣會整合Dropbox API,允許我們監控家用監控系統並且當我們的系統檢測到運動時,獲取實時更新資料。

相關推薦

Python OpenCV 檢測跟蹤運動物件

這個該死的傢伙。我就知道他偷了我最後一罐啤酒! 對於一個男人來講,這些話永遠都不該說。但是當我關上冰箱門的時候,我憤怒地嘆息,感到厭惡,自言自語地說了這些。 你看,我花了12個小時寫了這篇將要發表的文章《PyImageSearch Gurus course》。我的腦子都

Python上使用OpenCV檢測跟蹤行人

這是一個跟蹤行人的演示程式,由於他們的動作更容易預測,因次行人檢測跟蹤問題會更容易一些。 該專案使用OpenCV 3.1.0和Python。效果視訊網址:https://www.youtube.com/watch?v=ZYgb9e5i_JM 實現Demo如下: # =========

Python OpenCV 檢測圖片上的條形碼(轉載)

  原文地址:http://python.jobbole.com/80448/ 假設我們要檢測下圖中的條形碼: 圖1:包含條形碼的示例圖片 現在讓我們開始寫點程式碼,新建一個檔案,命名為detect_barcode.py,開啟並編碼: Python 1&nbs

直接可以PythonOpenCV檢測及分割影象的目標區域例子

第一天 老師:你知道麼,今天有人問了我一個問題。 ~.我:什麼? 老師:他說很難。 ~.我:關於什麼的? 老師:影象處理。 ~.我:喔,你說說看,我確實做了不少影象處理的東西(心裡默唸,你不知知道你給過我多少影象嗎?) 老師:好嘞!在用深度學習的時

使用PythonOpenCV檢測影象中的物體並將物體裁剪下來

介紹 碩士階段的畢設是關於昆蟲影象分類的,程式碼寫到一半,上週五導師又給我新的昆蟲圖片資料集了,新圖片中很多圖片很大,但是圖片中的昆蟲卻很小,所以我就想著先處理一下圖片,把圖片中的昆蟲裁剪下來,這樣除去大部分無關背景,應該可以提高識別率。 原圖片舉例(將紅色

python爬蟲爬取登陸github

一 利用API簡單爬取 利用GitHub提供的API爬取前十個star數量最多的Python庫 GitHub提供了很多專門為爬蟲準備的API介面,通過介面可以爬取到便捷,易處理的資訊。(這是GitHub官網的各種api介紹)     使用到的庫 import re

OpenCV PythonOpenCV安裝入門最強詳細攻略

                一、關於OpenCV簡介       OpenCV是一個基於BSD許可(開源)發行的跨平臺計算機視覺庫,可以執行在Linux、Windows、Android和Mac OS作業系統上。它輕量級而且高效——由一系列 C 函式和少量 C++ 類構成,同時提供了Python、Ruby、

python解決 圖論 多項式的矩陣、特徵值、特徵多項式的相互轉化

馬上就要畢業了,不少的同學都在緊鑼密鼓的寫論文,對於一些數學系或者理科生來說,經常會遇到一些圖論問題,根據圖形來求出這個圖的矩陣、圖的度……那麼我們知道很多的圖形都能轉化成特徵多項式,那麼今天我們就來寫一個程式,使其通過圖形的矩陣來求多項式。 開始 其實大部分數學工具(Matlab)都能

Python 快速實現 HTTP FTP 伺服器

用 Python 快速實現 HTTP 伺服器 有時你需臨時搭建一個簡單的 Web Server,但你又不想去安裝 Apache、Nginx 等這類功能較複雜的 HTTP 服務程式時。這時可以使用 Python 內建的 SimpleHTTPServer 模組快速搭建一個簡單的 HTTP 伺服器。 Si

python進行圖片處理特徵提取

原文來自:http://www.analyticsvidhya.com/blog/2015/01/basics-image-processing-feature-extraction-python/ 毫無疑問,上面的那副圖畫看起來像一幅電腦背景圖片。這些都歸功於我的

python畫三角形外接圓內切圓

剛看了《最強大腦》中英對決,其中難度最大的專案需要選手先腦補泰森多邊形,再找出完全相同的兩個泰森多邊形。在驚呆且感嘆自身頭腦愚笨的同時,不免手癢想要藉助電腦弄個圖出來看看,閒來無事吹吹NB也是極好的。 今天先來畫畫外接圓和內切圓,留個大坑後面來填 :-]。

Python建設企業認證許可權控制平臺

目前大家對Python的瞭解更多來源是資料分析、AI、運維工具開發,在行業中使用Python進行web開發,同樣也是非常受歡迎的,例如:FaceBook,豆瓣,知乎,餓了麼等等,本文主要是介紹是利用Python進行web開發企業統一使用者認證和許可權控制平臺,提供使用者管理

Python-迴文檢測布林操作(and,or,not)

下面是兩個函式都是做迴文檢測的,isP()是精簡版,isP1()是解析版. def isP(s): if len(s)<=1:return True else:return

Python對XML讀取處理

簡介 XML不是為了方便閱讀而設計,而是為了編碼為資料。當有一些文字有很多文件,可以用編碼的方式使3一些文字便與處理。 設計原則 提供不依賴平臺的資料轉移 方便的編寫讀寫XML程式 資料格式是可驗證的 便於人工閱讀 為了支援各種應用而設計 練習:提取XML資

使用OpenCV檢測追蹤車輛

使用高斯混合模型(BackgroundSubtractorMOG2)對背景建模,提取出前景使用中值濾波去掉椒鹽噪聲,再閉運算和開運算填充空洞使用cvBlob庫追蹤車輛,我稍微修改了cvBlob原始碼來通過編譯由於要對背景建模,這個方法要求背景是靜止的另外不同車輛白色區域不能連

python實現英文字母相應序數轉換的方法

第一步:字母轉數字 英文字母轉對應數字相對簡單,可以在命令行輸入一行需要轉換的英文字母,然後對每一個字母在整個字母表中匹配,並返

4 python進行OpenCV實戰之影象變換1(平移)

前言 到目前為止,經過前幾節的介紹,我們已經有了一個堅實的基礎去做一些影象處理,在本節我們先將介紹影象變換中的平移,為後面幾節學習影象變換中的旋轉、改變大小、映象、裁剪打下一個好的基礎 1 平移 1.1 平移基本操作 新建 translatio

2 python進行OpenCV實戰之影象基本知識

前言 在這一節,我們將學習影象的基本構成單元——畫素,我們將詳細的探討什麼是畫素?畫素是如何使用來構成影象的?然後學習如何通過OpenCV來獲取和操縱畫素。 1 什麼是畫素 所有的影象都包含一組畫素,畫素是影象的原始構建塊。 沒有比畫素更細的單位了。

3 python進行OpenCV實戰之畫圖(直線,矩形,圓形)

前言 在上一節我們通過使用NumPy的陣列分割成功的在我們的影象上畫了一個綠色的方塊,但是如果我們想畫一個單一的線條或者圓圈該怎麼辦呢?NumPy沒有提供相關的功能,但是OpenCV提供了相關的函式,在本節就將為大家介紹三個基本的OpenCV畫圖方法:

5 python進行OpenCV實戰之影象變換2(旋轉)

前言 最近有些其他事情,一週未更新了,實在抱歉。以後爭取,每週多更新幾次。雖然也不知道能有多少人看到,但是也算自己的一種堅持吧! 1 旋轉 1.1 旋轉基本操作 旋轉的概念正如我們平常聽見的一樣:將圖片選裝x度。我們先通過多少度來旋轉圖片,然後我們將