基於 TensorFlow 、OpenCV 和 Docker 的實時視頻目標檢測

雷鋒網按:本文為雷鋒網字幕組編譯的技術博客,原標題 Real-time and video processing object detection using Tensorflow, OpenCV and Docker,作者為 Léo Beaucourt 。

翻譯 | 於志鵬 徐普 校對 | 陶玉龍 整理 | 孔令雙

在本文中,我將介紹如何在 Docker 容器中使用 Tensorflow Object-detection API 來執行實時(網絡攝像頭)和視頻的目標檢測。我使用 OpenCV 和 python3 的多任務處理庫 multiprocessing、多線程庫 multi-threading。

我會重點描述我在搭建過程中遇到的問題,以及我的解決方案 (有些還未解決)。完整的代碼在這裡 my Github:

https://github.com/lbeaucourt/Object-detection

基于 TensorFlow 、OpenCV 和 Docker 的实时视频目标检测

使用Youtube視頻進行視頻處理測試

動機

我們從 Dat Tran 這篇文章開始挑戰實時目標檢測。我將使用 python 的 multiprocessing 庫,增加處理網絡攝像頭時的 FPS。為了進一步提高可移植性,我將項目集成到 Docker 容器中。不過處理進出容器的視頻流可能會有一點麻煩。

此外,在次項目我還添加了一個視頻後處理功能,同樣使用 multiprocessing 庫來減少處理時間(使用 Tensorflow 原始目標檢測 API 處理時間會非常長)。

實時和視頻目標識別都可以在我的個人筆記本電腦上以高性能運行,僅使用 8GB CPU。

Docker在數據科學中的應用

我不在這裡描述 Tensorflow 目標檢測 API 的實現,因為相關的文檔很多。我將展示數據科學家在日常工作中如何使用 Docker。注意,我會使用 Tensorflow 的經典 ssd_mobilenet_v2_coco 模型來提高性能。先將模型文件(.pb 文件)和相應的標籤映射文件複製到本地,後面可能會用到。

我認為使用 Docker 應是當今數據科學家的必備技能。在數據科學和機器學習領域,每週都會發布許多新的算法,工具和程序,直接在你的計算機目錄上安裝調試這些代碼、程序會讓系統變得凌亂不堪。為了防止這種情況,我使用 Docker 容器來創建我的數據科學工作區將程序部署在容器中。

你可以在我的代碼庫中找到這個項目的 Dockerfile。以下是我安裝配置 Tensorflow 目標檢測的方法(按照官方安裝指南):

# Install tensorFlow

RUN pip install -U tensorflow

# Install tensorflow models object detection

RUN git clone https://github.com/tensorflow/models /usr/local/lib/python3.5/dist-packages/tensorflow/models

RUN apt-get install -y protobuf-compiler python-pil python-lxml python-tk

#Set TF object detection available

ENV PYTHONPATH "$PYTHONPATH:/usr/local/lib/python3.5/dist-packages/tensorflow/models/research:/usr/local/lib/python3.5/dist-packages/tensorflow/models/research/slim"

RUN cd /usr/local/lib/python3.5/dist-packages/tensorflow/models/research && protoc object_detection/protos/*.proto --python_out=.

安裝 OpenCv 並編譯:

# Install OpenCV

RUN git clone https://github.com/opencv/opencv.git /usr/local/src/opencv

RUN cd /usr/local/src/opencv/ && mkdir build

RUN cd /usr/local/src/opencv/build && cmake -D CMAKE_INSTALL_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local/ .. && make -j4 && make install

編譯鏡像的時候有點長,之後就可以快速的調用

實時圖像目標檢測

我首先嚐試將目標檢測應用於我的網絡攝像頭。在 Dat Tran 的文章中有這部分的詳細描述。難點在於將網絡攝像頭流發送到 docker 容器並恢復輸出流以使用 X11 服務器顯示它。

將視頻流發送到容器

Linux 系統可以在/ dev /目錄中找到攝像頭設備,並可以將其作為文件進行操作。通常筆記本電腦攝像頭是「0」設備。要將其數據流發送到 docker 容器,請在運行 docker 鏡像時使用 device 參數:

docker run --device=/dev/video0

對於 Mac 和 Windows 用戶,將網絡攝像頭流發送到容器的方式並不像 Linux 那樣簡單(儘管 Mac 基於 Unix)。我不在這裡過多介紹,可以查閱相關文檔,只提一下 Windows 用戶的解決方案是使用 Virtual Box 啟動 docker 容器。

在容器中恢復視頻流

解決這個問題我花了一段時間(然而並沒有完美解決)。我找到了一些使用 Docker 圖形界面的資料,here。特別是介紹了將容器連接到主機的 X 服務以顯示內容

你必須開啟 xhost,以便容器可以通過讀寫 X11 unix 套接字來正常的顯示內容。首先設置 X 服務器主機的權限(有一定安全隱患)讓 docker 訪問它:

xhost +local:docker

在完成項目後,應當恢復默認設置

xhost -local:docker

然後創建兩個環境變量 XSOCK 和 XAUTH:

XSOCK=/tmp/.X11-unix

XAUTH=/tmp/.docker.xauth

第一個環境變量引用 X11 unix 套接字,第二個引用 X 驗證文件配置適當的權限:

xauth nlist $DISPLAY | sed -e 's/^..../ffff/' | xauth -f $XAUTH nmerge -

最後,我們只需要更新我們的 docker run 命令。傳入我們的 DISPLAY 環境變量,為 X11 Unix 套接字增加一個卷,併為 X 身份驗證文件增加一個名為 XAUTHORITY 的環境變量,並讓該變量指向它:

docker run -it --rm --device=/dev/video0 -e DISPLAY=$DISPLAY -v $XSOCK:$XSOCK -v $XAUTH:$XAUTH -e XAUTHORITY=$XAUTH

現在我們可以運行 docker 容器看看效果

基于 TensorFlow 、OpenCV 和 Docker 的实时视频目标检测

目標檢測結果 (我是個害羞的人⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄)

儘管主機具有X服務配置,我仍不能完全刪除代碼中的bug。在OpenCV中 需要通過調用python 腳本(init-openCV.py)來進行初始化,即使用函數cv2.imshow 。用這種方法我得到了如下的錯誤消息:

The program 'frame' received an X Window System error.

然後,它可能調用主要python 腳本(my-object-detection.py) 並且將視頻流傳送到主機進行展示。我對使用第一個python 腳本去初始化X11系統的結果不是很滿意,但是目前我還沒有找到解決這個問題的方法。

後來補充:我最終(在偶然間)發現這個問題的解決方法,通過使用OpenCV (3.4.1) 這個穩定版本替代本地克隆的git庫。因此現在在主流python 腳本之前沒有必要調用 init openCV.py

視頻處理

為了能通過我的攝像頭實時運行目標檢測API ,我使用線程和多進程處理的python 庫。一個線程用於讀取攝像頭視頻流。視頻幀被放進一個隊列通過工作池去處理(Tensorflow目標檢測運行的地方)。

對於視頻處理而言,它不可能使用線程,因為所有的視頻幀都是在工作單元能將目標檢測應用在隊列第一幀之前被讀取。當輸入隊列滿後被讀取的視頻幀就會被丟失。使用大量工作單元和隊列可能可以解決這個問題(伴隨巨大的算力消耗)

簡單隊列的另外一個問題是,由於分析時間的不斷變化,視頻幀在輸出隊列中不是按照與輸入隊列相同的順序。

為了增加視頻處理功能,我刪掉了讀取幀率的線程。作為一個替代,我使用下面的代碼來讀取幀率。

while True:

# Check input queue is not full

if not input_q.full:

# Read frame and store in input queue

ret, frame = vs.read

if ret:

input_q.put((int(vs.get(cv2.CAP_PROP_POS_FRAMES)),frame))

如果輸入隊列沒滿,下一幀視頻從視頻流中讀取並且放進隊列中。否則,當視頻幀沒有從輸入隊列獲取時不會處理任何事情。

為了解決幀率順序的問題,我使用瞭如下這種優先隊列作為第二輸入隊列:

1. 視頻幀帶著對應的視頻幀編號被讀取並放入輸入隊列中(實際上是一個python 列表對象放入了序列)。

2. 然後,工作單元從輸入隊列中提取視頻幀,處理後將它們放入第一個輸出隊列(依然帶著它們相關的視頻幀編號)。

while True:

frame = input_q.get

frame_rgb = cv2.cvtColor(frame[1], cv2.COLOR_BGR2RGB)

output_q.put((frame[0], detect_objects(frame_rgb, sess, detection_graph)))

3. 如果輸出隊列不為空,視頻幀帶著它們相應的優先視頻幀編號被抽取並放入優先隊列。優先隊列的大小被設置為其它隊列的三倍。

# Check output queue is not empty

if not output_q.empty:

# Recover treated frame in output queue and feed priority queue

output_pq.put(output_q.get)

4. 最後,如果輸出優先隊列不為空,則取出有最高優先編號的視頻幀(最小的優先編號)(這是標準的優先隊列工作)。如果優先級編號對應於預期的編號,視頻幀被加入輸出視頻流(並且根據需要寫入),其它的視頻幀則被放回優先隊列。

# Check output priority queue is not empty

if not output_pq.empty:

prior, output_frame = output_pq.get

if prior > countWriteFrame:

output_pq.put((prior, output_frame))

else:

countWriteFrame = countWriteFrame + 1

# Do something with your frame

為了停止這個過程,我檢查所有的隊列為空,並且所有的視頻幀已經從視頻流中抽取:

if((not ret) & input_q.empty &

output_q.empty & output_pq.empty):

break

總結

在這篇文章中,我展示瞭如何使用docker來實現Tensorflow的實時目標檢測項目。如上所述,docker是測試新數據科學工具最安全的方法,同時可以將解決方案打包給用戶。我也將如何採用來自Dat Tran 原始的python 腳本利用多進程去進行視頻處理展示給你。

謝謝你從頭到尾閱讀這篇文章。如上所述,這個項目有許多可以提高的地方。如果您有任何意見,請不要猶豫立刻告知我,我總是熱衷得到建議或評論。

原文鏈接:

https://towardsdatascience.com/real-time-and-video-processing-object-detection-using-tensorflow-opencv-and-docker-2be1694726e5

雷鋒網字幕組編譯。

號外號外~

一個專注於

AI技術發展和AI工程師成長的求知求職社區

誕生啦!

歡迎大家訪問以下鏈接或者掃碼體驗

https://club.leiphone.com/page/home


分享到:


相關文章: