深度學習 + OpenCV,Python實現實時視訊目標檢測

李亞洲發表於2017-09-21

使用 OpenCV 和 Python 上對實時視訊流進行深度學習目標檢測是非常簡單的,我們只需要組合一些合適的程式碼,接入實時視訊,隨後加入原有的目標檢測功能。

在本文中我們將學習如何擴充套件原有的目標檢測專案,使用深度學習和 OpenCV 將應用範圍擴充套件到實時視訊流和視訊檔案中。這個任務會通過 VideoStream 類來完成。

  • 深度學習目標檢測教程:http://www.pyimagesearch.com/2017/09/11/object-detection-with-deep-learning-and-opencv/
  • VideoStream 類教程:http://www.pyimagesearch.com/2016/01/04/unifying-picamera-and-cv2-videocapture-into-a-single-class-with-opencv/

現在,我們將開始把深度學習+目標檢測的程式碼應用於視訊流中,同時測量 FPS 處理速度。

使用深度學習和 OpenCV 進行視訊目標檢測

為了構建基於 OpenCV 深度學習的實時目標檢測器,我們需要有效地接入攝像頭/視訊流,並將目標檢測應用到每一幀裡。

首先,我們開啟一個新檔案,將其命名為 real_time_object_detection.py,隨後加入以下程式碼:

# import the necessary packages
from imutils.video import VideoStream
from imutils.video import FPS
import numpy as np
import argparse
import imutils
import time
import cv2

我們從第 2-8 行開始匯入封包。在此之前,你需要 imutils 和 OpenCV 3.3。在系統設定上,你只需要以預設設定安裝 OpenCV 即可(同時確保你遵循了所有 Python 虛擬環境命令)。

Note:請確保自己下載和安裝的是 OpenCV 3.3(或更新版本)和 OpenCV-contrib 版本(適用於 OpenCV 3.3),以保證其中包含有深度神經網路模組。

下面,我們將解析這些命令列引數:

# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-p", "--prototxt", required=True,
	help="path to Caffe'deploy'prototxt file")
ap.add_argument("-m", "--model", required=True,
	help="path to Caffe pre-trained model")
ap.add_argument("-c", "--confidence", type=float, default=0.2,
	help="minimum probability to filter weak detections")
args = vars(ap.parse_args())

與此前的目標檢測專案相比,我們不需要影象引數,因為在這裡我們處理的是視訊流和視訊——除了以下引數保持不變:

--prototxt:Caffe prototxt 檔案路徑。

--model:預訓練模型的路徑。

--confidence:過濾弱檢測的最小概率閾值,預設值為 20%。

隨後,我們初始化類列表和顏色集:

# initialize the list of class labels MobileNet SSD was trained to
# detect, then generate a set of bounding box colors for each class
CLASSES = ["background", "aeroplane", "bicycle", "bird", "boat",
	"bottle", "bus", "car", "cat", "chair", "cow", "diningtable",
	"dog", "horse", "motorbike", "person", "pottedplant", "sheep",
	"sofa", "train", "tvmonitor"]
COLORS = np.random.uniform(0, 255, size=(len(CLASSES), 3))

在第 22-26 行,我們初始化 CLASS 標籤,和相應的隨機 COLORS。有關這些類的詳細資訊(以及網路的訓練方式),請參考:http://www.pyimagesearch.com/2017/09/11/object-detection-with-deep-learning-and-opencv/

現在,我們載入自己的模型,並設定自己的視訊流:

# load our serialized model from disk
print("[INFO] loading model...")
net = cv2.dnn.readNetFromCaffe(args["prototxt"], args["model"])
 
# initialize the video stream, allow the cammera sensor to warmup,
# and initialize the FPS counter
print("[INFO] starting video stream...")
vs = VideoStream(src=1).start()
time.sleep(2.0)
fps = FPS().start()

我們載入自己的序列化模型,提供對自己的 prototxt 和模型檔案的引用(第 30 行),可以看到在 OpenCV 3.3 中,這非常簡單。

下一步,我們初始化視訊流(來源可以是視訊檔案或攝像頭)。首先,我們啟動 VideoStream(第 35 行),隨後等待相機啟動(第 36 行),最後開始每秒幀數計算(第 37 行)。VideoStream 和 FPS 類是 imutils 包的一部分。

現在,讓我們遍歷每一幀(如果你對速度要求很高,也可以跳過一些幀):

# loop over the frames from the video stream
while True:
	# grab the frame from the threaded video stream and resize it
	# to have a maximum width of 400 pixels
	frame = vs.read()
	frame = imutils.resize(frame, width=400)
 
	# grab the frame dimensions and convert it to a blob
	(h, w) = frame.shape[:2]
	blob = cv2.dnn.blobFromImage(frame, 0.007843, (300, 300), 127.5)
 
	# pass the blob through the network and obtain the detections and
	# predictions
	net.setInput(blob)
	detections = net.forward()

首先,我們從視訊流中讀取一幀(第 43 行),隨後調整它的大小(第 44 行)。由於我們隨後會需要寬度和高度,所以我們在第 47 行上進行抓取。隨後將 frame 轉換為一個有 dnn 模組的 blob(第 48 行)。

現在,我們設定 blob 為神經網路的輸入(第 52 行),通過 net 傳遞輸入(第 53 行),這給我們提供了 detections。

這時,我們已經在輸入幀中檢測到了目標,現在是時候看看置信度的值,以判斷我們能否在目標周圍繪製邊界框和標籤了:

# loop over the detections
	for i in np.arange(0, detections.shape[2]):
		# extract the confidence (i.e., probability) associated with
		# the prediction
		confidence = detections[0, 0, i, 2]
 
		# filter out weak detections by ensuring the `confidence` is
		# greater than the minimum confidence
		if confidence > args["confidence"]:
			# extract the index of the class label from the
			# `detections`, then compute the (x, y)-coordinates of
			# the bounding box for the object
			idx = int(detections[0, 0, i, 1])
			box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
			(startX, startY, endX, endY) = box.astype("int")
 
			# draw the prediction on the frame
			label = "{}: {:.2f}%".format(CLASSES[idx],
				confidence * 100)
			cv2.rectangle(frame, (startX, startY), (endX, endY),
				COLORS[idx], 2)
			y = startY - 15 if startY - 15 > 15 else startY + 15
			cv2.putText(frame, label, (startX, y),
				cv2.FONT_HERSHEY_SIMPLEX, 0.5, COLORS[idx], 2)

我們首先在 detections 內迴圈,記住一個影象中可以檢測到多個目標。我們還需要檢查每次檢測的置信度(即概率)。如果置信度足夠高(高於閾值),那麼我們將在終端展示預測,並以文字和彩色邊界框的形式對影象作出預測。讓我們逐行來看一下:

在 detections 內迴圈,首先我們提取 confidence 值(第 59 行)。

如果 confidence 高於最低閾值(第 63 行),那麼我們提取類標籤索引(第 67 行),並計算檢測到的目標的座標(第 68 行)。

然後,我們提取邊界框的 (x, y) 座標(第 69 行),之後將用於繪製矩形和文字。

我們構建一個文字 label,包含 CLASS 名稱和 confidence(第 72、73 行)。

我們還要使用類顏色和之前提取的 (x, y) 座標在物體周圍繪製彩色矩形(第 74、75 行)。

通常,我們希望標籤出現在矩形上方,但是如果沒有空間,我們將在矩形頂部稍下的位置展示標籤(第 76 行)。

最後,我們使用剛才計算出的 y 值將彩色文字置於幀上(第 77、78 行)。

幀捕捉迴圈剩餘的步驟還包括:(1)展示幀;(2)檢查 quit 鍵;(3)更新 fps 計數器:

# show the output frame
	cv2.imshow("Frame", frame)
	key = cv2.waitKey(1) & 0xFF
 
	# if the `q` key was pressed, break from the loop
	if key == ord("q"):
		break
 
	# update the FPS counter
	fps.update()

上述程式碼塊簡單明瞭,首先我們展示幀(第 81 行),然後找到特定按鍵(第 82 行),同時檢查「q」鍵(代表「quit」)是否按下。如果已經按下,則我們退出幀捕捉迴圈(第 85、86 行)。最後更新 fps 計數器(第 89 行)。

如果我們退出了迴圈(「q」鍵或視訊流結束),我們還要處理這些:

# stop the timer and display FPS information
fps.stop()
print("[INFO] elapsed time: {:.2f}".format(fps.elapsed()))
print("[INFO] approx. FPS: {:.2f}".format(fps.fps()))
 
# do a bit of cleanup
cv2.destroyAllWindows()
vs.stop()

當我們跳出(exit)迴圈,fps 計數器 停止(第 92 行),每秒幀數的資訊向終端輸出(第 93、94 行)。

我們關閉視窗(第 97 行),然後停止視訊流(第 98 行)。

如果你到了這一步,那就可以做好準備用自己的網路攝像頭試試看它是如何工作的了。我們來看下一部分。

實時深度學習目標檢測的結果

為了實時深度學習目標檢測器正常執行,確保你使用本指南「Downloads」部分中的示例程式碼和預訓練的卷積神經網路。(請開啟原文連結,進入「Downloads」部分,輸入自己的郵箱地址,獲取所需程式碼和其他資料。)

開啟終端,執行下列命令:

$ python real_time_object_detection.py \
	--prototxt MobileNetSSD_deploy.prototxt.txt \
	--model MobileNetSSD_deploy.caffemodel
[INFO] loading model...
[INFO] starting video stream...
[INFO] elapsed time: 55.07
[INFO] approx. FPS: 6.54

如果 OpenCV 能夠訪問你的攝像頭,你可以看到帶有檢測到的目標的輸出視訊幀。我對樣本視訊使用了深度學習目標檢測,結果如下:

深度學習 + OpenCV,Python實現實時視訊目標檢測

圖 1:使用深度學習和 OpenCV + Python 進行實時目標檢測的短片。


注意深度學習目標檢測器不僅能夠檢測到人,還能檢測到人坐著的沙發和旁邊的椅子——所有都是實時檢測到的!

總結

今天的部落格中,我們學習瞭如何使用深度學習 + OpenCV + 視訊流來執行實時目標檢測。我們通過下列兩個教程完成了這一目標:

1. 使用深度學習和 OpenCV 進行目標檢測(http://www.pyimagesearch.com/2017/09/11/object-detection-with-deep-learning-and-opencv/)

2. 在 OpenCV 上進行高效、執行緒化的視訊流(http://www.pyimagesearch.com/2016/01/04/unifying-picamera-and-cv2-videocapture-into-a-single-class-with-opencv/)

最終結果是基於深度學習的目標檢測器可以處理 6-8 個 FPS 的視訊(當然,這也取決於你的系統速度)。

你還可以通過以下途徑進一步提升速度:

1. 跳過幀。

2. 使用 MobileNet 的不同變體(速度更快,但是準確率下降)。

3. 使用 SqueezeNet 的量子化變體(我還未對此進行測試,但是我想應該會更快,因為它的網路足跡更小)。

相關文章