# 前言 RCNN開創了物件偵測的先鋒,接續又出現了Fast RCNN與Faster RCNN,但以上的演算法具有以下缺點:使用選擇性搜索(Selective Search)、不屬於端到端的演算法、使用多種模型等。而在2016年,[YOLO(You Only Look Once)](https://www.cv-foundation.org/openaccess/content_cvpr_2016/papers/Redmon_You_Only_Look_CVPR_2016_paper.pdf)的問世解決了上述提到的問題。 原本的物件偵測任務皆是透過分類器進行分類,但在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演進](https://medium.com/ching-i/yolo演進-2-85ee99d114a1)、[一文弄懂YOLO算法](https://zhuanlan.zhihu.com/p/52661111)、[圖解一階段物件偵測算法](https://youtu.be/sq_OfIhb5Oc?si=_DSNkeWUBJBch63T)。 # 說明 **本篇文章的目的:** - 透過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](https://github.com/ultralytics/yolov5.git)。但為了避免干擾到原本的環境,建議可以先建立一個虛擬環境,有以下兩種方法: - 第一種:Python ```python= # 建立虛擬環境 python -m venv <venv name> # 啟動環境 source <venv name>/bin/activate ``` - 第二種:anaconda ```python= # 建立虛擬環境 conda create --name <venv name> # 啟動環境 conda activate <venv name> ``` 安裝虛擬環境後,就可以安裝YOLO囉 ```command= !git clone https://github.com/ultralytics/yolov5 ``` 安裝YOLO v5所需的環境與套件 ```= !pip3 install -r ./yolov5/requirements.txt ``` 接著就可以```import```套件了 ```python= import torch import numpy as np import matplotlib.pyplot as plt import cv2 ``` ## Load Model 第二步就是要載入YOLO模型 ```python= model = torch.hub.load("ultralytics/yolov5", "yolov5s") ``` - [torch hub](https://pytorch.org/hub/)存有許多官方與非官方所開發的(預訓練)模型,也可以發表自己的模型 可以將模型的架構印出來,如下圖所示 ```python= model ``` ==YOLO模型架構== ![架構](https://hackmd.io/_uploads/BkDe1GEMp.png) ## Make Detections 載入模型後,就可以載入影像來做測試,確認模型能夠正常運作 ```python= img = "https://media.cntraveler.com/photos/53e2f41cdddaa35c30f66775/16:9/w_1280,c_limit/highway-traffic.jpg" result = model(result) result.print() ``` ==output結果如下圖所示== ![output1](https://hackmd.io/_uploads/SJ97lGVM6.png) - 上圖的結果包含了影像大小、偵測到的物件、偵測速度等資訊 <br> 接著我們可以透過```render()```把結果畫出來 ```python= %matplotlib inline plt.imshow(np.squeeze(result.render())) plt.show() ``` ==偵測結果如下圖所示== ![output2](https://hackmd.io/_uploads/rJPuZGNzT.png =70%x) **NOTE:** ```python= result ``` 如果直接執行上述程式,只是一個base class,==如下圖所示== ![output3](https://hackmd.io/_uploads/ByVvzf4Ma.png) ```python= result. ``` 若輸入上述的程式,並按```tab```,可看到很多```function```,==如下圖所示== ![output4](https://hackmd.io/_uploads/rkDl7zVGa.png =50%x) 而```result.render()```是會回傳將detection畫上影像的結果,==如下圖所示== ![output5](https://hackmd.io/_uploads/BJpamzEMT.png =50%x) 由於```result.render()```是list,所以無法直接透過```matplotlib```畫出,所以要先轉成```array```。但直接透過```np.array()```轉,其shape會多出一個batch size的參數。為了得到```(720, 1280, 3)```的影像,就須透過```np.squeeze()```。 <br> ## Real Time Detections 確認YOLO能夠執行之後,我們可以先玩看看real time的偵測。透過```cv2.VideioCapture()```來打開攝影機,並擷取到影像。 ```python= # 調用攝影機 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標記。 - 收集影像 ```python= # 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](https://hackmd.io/_uploads/B14pwMVfa.png =70%x) ## LabelImg 接著我們需要安裝[LabelImg](https://github.com/HumanSignal/labelImg.git),建議也可以專門使用LabelImg的虛擬環境。 在安裝的過程中遇到很多問題,照著[LabelImg](https://github.com/HumanSignal/labelImg.git)的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](https://hackmd.io/_uploads/Sy7AcfVfa.png) ==進入標記之前,先給大家看一下整個資料夾的結構== ![資料夾結構](https://hackmd.io/_uploads/SJXPHQ4f6.png =70%x) data資料夾下包含: - images:存放醒著和打瞌睡的影像 - labels:存放透過LabelImg標記的資訊 **接著就可以標記了,我使用兩張影像做示範** **快捷鍵:** * W => 建立Bounding box * A => 上一張 * D => 下一張 - 開啟影像資料夾 ![step1](https://hackmd.io/_uploads/r1tLCMVM6.png) - 設定存放目錄 ![step2](https://hackmd.io/_uploads/BJOoAGNf6.png) - 設定為YOLO格式 ![step3](https://hackmd.io/_uploads/ByOn1X4zT.png) - 按w開始標記,此影像為drowsy,**標記完記得儲存** ![step4](https://hackmd.io/_uploads/ryjbx7Efa.png) - 接著就會在labels資料夾下看到新增的檔案 ![step5](https://hackmd.io/_uploads/ry_UlXEG6.png =70%x) * classes.txt:類別檔 * 剛標記的Bouding Box位置 - 按w開始標記,此影像為awake ![step6](https://hackmd.io/_uploads/H10N-mEGp.png) 接著按照這些步驟,把所有的影像都標記完就可以了! ## Train from Scratch 接著就可以透過剛標記好的影像進行訓練了,終於來到這一步了!!!! ```python= !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 接著就可以把剛訓練完的模型拿來做測試了 - 影像測試 ```python= 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](https://hackmd.io/_uploads/S1HxrQEzp.jpg) - Video測試 ```python= 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](https://github.com/ChrisCodeNation/Drowsiness-Detection-using-Yolo.git) # 參考資料 [YOLO v5](https://github.com/ultralytics/yolov5.git) [LabelImg](https://github.com/HumanSignal/labelImg.git) [Deep Drowsiness Detection using YOLO, Pytorch and Python](https://youtu.be/tFNJGim3FXw?si=OZH1QIqIxVB4e5vM)