Try   HackMD

前言

RCNN開創了物件偵測的先鋒,接續又出現了Fast RCNN與Faster RCNN,但以上的演算法具有以下缺點:使用選擇性搜索(Selective Search)、不屬於端到端的演算法、使用多種模型等。而在2016年,YOLO(You Only Look Once)的問世解決了上述提到的問題。

原本的物件偵測任務皆是透過分類器進行分類,但在YOLO中則是將其視為回歸任務。YOLO將輸入影像切割成SxS的grid cell,並從這些grid cell中找出邊界框(Bounding Box)並計算出類別的機率。整個過程只使用了一個神經網路,因此可視為一個End-to-End的演算法。

這樣的架構使整個偵測過程十分快速,可達到即時每秒45幀。而另外一個較小型的Fast YOLO不僅可達到每秒155幀的執行速度,mAP ( Mean Average Precision ) 也是其他即時物件偵測系統的兩倍。之後作者也發表了一系列的YOLO模型(v2, v3,)。本篇文章不會深入介紹其背後架構與技術,而是專注於介紹如何將YOLO應用於實際技術中。如對YOLO有興趣的人可以參考:YOLO演進一文弄懂YOLO算法圖解一階段物件偵測算法

說明

本篇文章的目的:

  • 透過LabelImg標註自己的資料,並製作訓練集
  • 使用custom dataset建立打瞌睡偵測模型

How it works

  • Install and Import Dependencies
  • Load Model
  • Make Detections
  • Real Time Detections
  • Collect Images
  • LabelImg
  • Train from Scratch
  • Testing

Install and Import Dependencies

第一步要先安裝YOLO,我們使用的是ultralytics所釋出的YOLO v5。但為了避免干擾到原本的環境,建議可以先建立一個虛擬環境,有以下兩種方法:

  • 第一種:Python
# 建立虛擬環境 python -m venv <venv name> # 啟動環境 source <venv name>/bin/activate
  • 第二種:anaconda
# 建立虛擬環境 conda create --name <venv name> # 啟動環境 conda activate <venv name>

安裝虛擬環境後,就可以安裝YOLO囉

!git clone https://github.com/ultralytics/yolov5

安裝YOLO v5所需的環境與套件

!pip3 install -r ./yolov5/requirements.txt

接著就可以import套件了

import torch import numpy as np import matplotlib.pyplot as plt import cv2

Load Model

第二步就是要載入YOLO模型

model = torch.hub.load("ultralytics/yolov5", "yolov5s")
  • torch hub存有許多官方與非官方所開發的(預訓練)模型,也可以發表自己的模型

可以將模型的架構印出來,如下圖所示

model

YOLO模型架構

架構

Make Detections

載入模型後,就可以載入影像來做測試,確認模型能夠正常運作

img = "https://media.cntraveler.com/photos/53e2f41cdddaa35c30f66775/16:9/w_1280,c_limit/highway-traffic.jpg" result = model(result) result.print()

output結果如下圖所示

output1

  • 上圖的結果包含了影像大小、偵測到的物件、偵測速度等資訊

接著我們可以透過render()把結果畫出來

%matplotlib inline plt.imshow(np.squeeze(result.render())) plt.show()

偵測結果如下圖所示

output2

NOTE:

result

如果直接執行上述程式,只是一個base class,如下圖所示

output3

result.

若輸入上述的程式,並按tab,可看到很多function如下圖所示

output4

result.render()是會回傳將detection畫上影像的結果,如下圖所示

output5

由於result.render()是list,所以無法直接透過matplotlib畫出,所以要先轉成array。但直接透過np.array()轉,其shape會多出一個batch size的參數。為了得到(720, 1280, 3)的影像,就須透過np.squeeze()

Real Time Detections

確認YOLO能夠執行之後,我們可以先玩看看real time的偵測。透過cv2.VideioCapture()來打開攝影機,並擷取到影像。

# 調用攝影機 cap = cv2.VideoCapture(0) # 確認攝影機是否正常開啟 if not cap.isOpened(): print("Cannot Open Camera...") exit() while True # 讀取攝影機影像 ret, frame = cap.read() # 確認是否能正常讀取影像 if not ret: print("Cannot Receieve Frame...") break # make detections result = model(frame) cv2.imshow("YOLO", np.squeeze(result.render())) if cv2.waitKey(10) & 0xFF == ord('q'): break # 釋放攝影機,並關閉視窗 cap.release() cv2.destroyAllWindows()

Collect Images

接著我們就可以建立打瞌睡偵測模型了。但再建模型之前,先需要有打瞌睡和醒著兩種狀態的影像來建立資料集,所以需要先建立影像,再透過LabelImg標記。

  • 收集影像
# collect awake and drowsy images from camera import uuid # unique identifier to name the images import os import time # 影像存放位置 IMAGES_PATH = os.path.join('data', 'images') # data/images labels = ['awake', 'drowsy'] number_img = 20 cap = cv2.VideoCapture(0) # 確認camera正常 if not cap.isOpened(): print("Cannot Open Camera...") exit() for label in labels: print(f'Collect images for {label}') time.sleep(5) for num in range(number_img): print(f'Collect images for {label}, image number {num}') # 取得影像 ret, frame = cap.read() # 建立image path image_name = os.path.join(IMAGES_PATH, label+'.'+str(uuid.uuid1())+'.jpg') # 儲存影像 cv2.imwrite(image_name, frame) cv2.imshow('Image Collection', frame) # 2 seconds delay between capture time.sleep(2) if cv2.waitKey(10) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()
  • uuid可產生unique identifier,如下圖所示
    output6

LabelImg

接著我們需要安裝LabelImg,建議也可以專門使用LabelImg的虛擬環境。

在安裝的過程中遇到很多問題,照著LabelImg的Github也無法安裝成功,後來看了網路上的方法可以透過pip install labelImg的方式安裝,不需要像Github上的複雜步驟。

而虛擬環境一開始使用Python的方式安裝,但同樣也無法安裝完成,最後使用anaconda的方式,而我是使用max os,其他os並沒有試過,以下是我的安裝方式:

  • 安裝虛擬環境
# 建立虛擬環境 conda create --name to_label # 啟動虛擬環境 conda activate to_label
  • 安裝LabelImg
pip install labelImg
  • 安裝好後,輸入
labelImg

如果安裝成功會出現以下的畫面

labelimg

進入標記之前,先給大家看一下整個資料夾的結構

資料夾結構

data資料夾下包含:

  • images:存放醒著和打瞌睡的影像
  • labels:存放透過LabelImg標記的資訊

接著就可以標記了,我使用兩張影像做示範

快捷鍵:

  • W => 建立Bounding box
  • A => 上一張
  • D => 下一張
  • 開啟影像資料夾

    step1

  • 設定存放目錄

    step2

  • 設定為YOLO格式

    step3

  • 按w開始標記,此影像為drowsy,標記完記得儲存

    step4

  • 接著就會在labels資料夾下看到新增的檔案

    step5

    • classes.txt:類別檔
    • 剛標記的Bouding Box位置
  • 按w開始標記,此影像為awake

    step6

接著按照這些步驟,把所有的影像都標記完就可以了!

Train from Scratch

接著就可以透過剛標記好的影像進行訓練了,終於來到這一步了!!!!

!cd yolov5 && python train.py --img 320 --batch 16 --epoch 500 --data dataset.yaml --weights yolov5s.pt

上述的程式有一個yaml file需要建立,可以透過Visual Studio Code開啟檔案,並輸入以下資訊。建立完後,請將yaml file放入yolo v5資料夾下

# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]
path: ../data
train: images
val: images
test:

# Classes (80 COCO classes)
names:
  0: awake
  1: drowsy

訓練完後,在yolov5/runs/train/exp會看到所有剛訓練的資料,包含訓練完的權重、learning curve與相關結果等。

Testing

接著就可以把剛訓練完的模型拿來做測試了

  • 影像測試
model = torch.hub.load("ultralytics/yolov5", 'custom', path='yolov5/runs/train/exp3/weights/last.pt', force_reload=True) img = os.path.join('data', 'images', 'awake.8f423c6a-6f5a-11ee-8139-c4b301bfc059.jpg') result = model(img) result.print() %matplotlib inline plt.imshow(np.squeeze(result.render())) plt.show()

偵測結果如下

result

  • Video測試
cap = cv2.VideoCapture(0) while cap.isOpened(): ret, frame = cap.read() result = model(frame) cv2.imshow(np.squeeze(result.render())) if cv2.waitKey() & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()

Source Code Please Visit

參考資料

YOLO v5
LabelImg
Deep Drowsiness Detection using YOLO, Pytorch and Python