You Only Look Once

Table of Contents

Hits

1. 物件偵測:不只認得出來,還要框出位置

1.1. 影像分類 vs 物件偵測

我們之前學的 CNN 是做*影像分類*——給一張圖片,回答「這是什麼」。但真實世界中,一張照片裡通常有很多東西:

任務 輸入 輸出 範例
影像分類 一張圖片 一個類別標籤 「這是一隻貓」
物件偵測 一張圖片 多個物件的類別 + 邊界框位置 「左邊有貓,右邊有狗」
語意分割 一張圖片 每個像素的類別標籤 「哪些像素是貓、哪些是背景」

物件偵測(Object Detection) 就是在一張圖中找出所有物件,同時回答:

  1. 圖裡有什麼?(分類)
  2. 它在哪裡?(定位,用邊界框 bounding box 表示)

1.2. 為什麼叫「You Only Look Once」?

傳統的物件偵測方法(如 R-CNN)要看圖片好幾遍:先提取可能有物體的區域(幾千個候選框),再逐一分類。這就像你在教室裡找手機——先掃視一遍找到可能的位置,再逐一確認。

YOLO 的做法是:*只看一遍就搞定所有事*。它把整張圖片丟進一個神經網路,一次輸出所有物件的位置和類別。這就像開了天眼——一眼就看到教室裡所有物品的位置和名字。

這就是為什麼它叫 YOLO:You Only Look Once(你只需要看一次)。

2. YOLO 的歷史

YOLO 最早由 Joseph Redmon 於 2015 年提出,此後歷經多代演進1

年份 版本 作者/團隊 重要性
2015 YOLOv1 Joseph Redmon 開創性的即時物件偵測
2017 YOLOv2 Joseph Redmon 又名 YOLO9000
2018 YOLOv3 Joseph Redmon 多尺度偵測
2020 YOLOv4 Bochkovskiy, 王建堯(中研院) 台灣之光
2020 YOLOv5 Glen Jocher / Ultralytics PyTorch 實作
2022 YOLOv7 王建堯(中研院), Bochkovskiy 再次台灣之光
2023 YOLOv8 Ultralytics 目前最常用
2024 YOLOv9 王建堯(中研院), 葉儀晧(北科大) 三度台灣之光
2025 YOLOv12 Tian, Ye, Doermann 注意力機制架構

值得一提的是,YOLO 的原作者 Joseph Redmon 在 2020 年宣布退出電腦視覺研究,原因是他無法接受 YOLO 不斷被應用在軍事和監控方面。之後,中研院的王建堯團隊成為 YOLO 系列的主要推手之一,YOLOv4、v7、v9 都有台灣團隊的貢獻。

2.1. YOLO 工作原理

YOLO 把物件偵測當成一個*回歸問題*:

  1. 將圖片切成 S×S 的格子
  2. 每個格子預測 B 個邊界框(bounding box)和 C 個類別的機率
  3. 一次前向傳播就完成所有預測

這就像在一張方格紙上,每個格子負責偵測它周圍的物件,最後把所有格子的結果彙整起來。

3. YOLOv8 實作

3.1. 關於 YOLOv8

目前 YOLO 的最新版本為 v12,但為了在教室的 Mac Mini(沒有 GPU)上順暢執行,我們使用 YOLOv8 的輕量版本。

YOLOv8 由 Ultralytics 開發,提供三種功能2

  • 物件偵測(Object Detection)
  • 實例分割(Instance Segmentation)
  • 影像分類(Image Classification)

3.2. 模型大小選擇

名稱 大小 準確率 推理速度 適用情境
yolov8n 非常快 手機、嵌入式設備
yolov8s 通用快速識別任務
yolov8m 準確率與速度平衡
yolov8l 更高 準確率要求較高的任務
yolov8x 最大 最高 最慢 高準確率、伺服器端推論

3.3. 預設可辨識的 80 種物件

官方預訓練模型使用 COCO dataset 訓練,可以辨識 80 種常見物件:

1: from ultralytics import YOLO
2: model = YOLO('yolov8n.pt')
3: # 印出所有可辨識的類別
4: for i, name in model.names.items():
5:     print(f"  {i:2d}: {name}", end="")
6:     if (i + 1) % 5 == 0:
7:         print()
8: print()

包括人、動物(貓、狗、馬、牛⋯)、交通工具(汽車、公車、飛機⋯)、日常用品(手機、鍵盤、剪刀⋯)等。

3.4. 安裝套件

pip install ultralytics

4. 偵測靜態照片

4.1. 本機執行

 1: from ultralytics import YOLO
 2: 
 3: # 載入 YOLOv8 nano 模型(首次會自動下載)
 4: model = YOLO("yolov8n.pt")
 5: 
 6: # 對圖片進行推論(請換成你自己的圖片路徑)
 7: results = model("your_image.jpg")
 8: 
 9: # 顯示結果(會彈出視窗)
10: results[0].show()

4.2. 辨識網路圖片

1: from ultralytics import YOLO
2: model = YOLO("yolov8n.pt")
3: 
4: # 直接丟網址也可以!
5: results = model("https://ultralytics.com/images/bus.jpg")
6: results[0].show()

4.3. 在 Colab 執行

如果在 Google Colab 上執行:

!pip install ultralytics
1: from ultralytics import YOLO
2: model = YOLO("yolov8n.pt")
3: results = model("/content/your_image.png")

5. 即時偵測(Webcam)

這是 YOLO 最酷的功能——用攝影機*即時*偵測畫面中的所有物件。

以下程式碼需要在本機的 IDE(如 PyCharm)中執行,不是在 org-mode 裡:

 1: import cv2
 2: from ultralytics import YOLO
 3: 
 4: # 載入預訓練模型
 5: model = YOLO("yolov8n.pt")
 6: 
 7: # 啟用 webcam(0 = 預設攝影機)
 8: cap = cv2.VideoCapture(0)
 9: 
10: if not cap.isOpened():
11:     print("無法打開攝影機")
12:     exit()
13: 
14: while True:
15:     ret, frame = cap.read()
16:     if not ret:
17:         print("無法讀取畫面")
18:         break
19: 
20:     # 進行 YOLO 偵測
21:     results = model(frame, verbose=False)
22:     annotated_frame = results[0].plot()  # 畫上偵測結果
23: 
24:     # 顯示偵測結果
25:     cv2.imshow("YOLOv8 Live Detection", annotated_frame)
26: 
27:     # 按下 q 鍵離開
28:     if cv2.waitKey(1) & 0xFF == ord('q'):
29:         break
30: 
31: # 釋放資源
32: cap.release()
33: cv2.destroyAllWindows()

6. OpenCV 人臉偵測

OpenCV 也有內建的人臉偵測功能,使用 Haar Cascade 分類器:

 1: import cv2
 2: 
 3: # 載入 Haar 人臉偵測模型(OpenCV 內建)
 4: face_cascade = cv2.CascadeClassifier(
 5:     cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
 6: )
 7: 
 8: cap = cv2.VideoCapture(0)
 9: if not cap.isOpened():
10:     print("無法開啟攝影機")
11:     exit()
12: 
13: while True:
14:     ret, frame = cap.read()
15:     if not ret:
16:         break
17: 
18:     # 轉灰階(人臉偵測在灰階上進行)
19:     gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
20: 
21:     # 偵測人臉
22:     faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5)
23: 
24:     # 畫出人臉位置
25:     for (x, y, w, h) in faces:
26:         cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
27: 
28:     cv2.imshow("Face Detection (press q to quit)", frame)
29:     if cv2.waitKey(1) & 0xFF == ord('q'):
30:         break
31: 
32: cap.release()
33: cv2.destroyAllWindows()

6.1. YOLO vs OpenCV 比較

功能類型 YOLO OpenCV
物件偵測 強大,支援 80 類 較弱,需自行訓練
人臉偵測 需訓練或搭配套件 內建 Haar/DNN
人臉辨識 原生不支援 需搭配 dlib/face_recognition
執行效率 高(GPU 加速) 中(適合 CPU)
易用性 容易(Ultralytics 很友善) 較基礎(需手動設定)

7. YOLO + 人臉辨識

光是偵測到「這裡有一個人」還不夠,如果要知道「這個人是誰」,就需要結合 face_recognition 套件。

7.1. 流程

1. 事前準備:拍攝每個人的照片 → 提取臉部特徵 → 儲存為 encodings.pkl
2. 即時辨識:YOLO 偵測到人 → 擷取臉部區域 → face_recognition 比對 → 顯示姓名

7.2. 安裝套件

pip install face_recognition opencv-python

注意:=face_recognition= 只適合辨識*不戴口罩*的臉,因為它需要完整臉部特徵(包含鼻子和嘴巴)。

7.3. 步驟一:準備照片

各組組員準備 5~10 張不同角度、光線的自拍照片,命名規則為「姓名_編號.jpg」:

images/
├── James_1.jpg
├── James_2.jpg
├── ...
├── Vanessa_1.jpg
├── Vanessa_2.jpg
└── ...

7.4. 步驟二:儲存臉部特徵(save_encodings.py)

這支程式把所有照片的臉部特徵向量和姓名存成 pickle 檔,之後辨識時直接載入就好:

 1: # save_encodings.py
 2: import face_recognition
 3: import os, pickle
 4: from PIL import UnidentifiedImageError
 5: 
 6: known_encodings = []
 7: known_names = []
 8: 
 9: # ← 請改成你的照片資料夾路徑
10: IMAGE_DIR = "/Users/student/Desktop/images"
11: 
12: for file in os.listdir(IMAGE_DIR):
13:     if file.startswith(".") or not file.lower().endswith((".jpg", ".jpeg", ".png")):
14:         continue
15:     name = file.split("_")[0]  # 從檔名取出姓名
16:     try:
17:         img = face_recognition.load_image_file(os.path.join(IMAGE_DIR, file))
18:         encodings = face_recognition.face_encodings(img)
19:         if encodings:
20:             known_encodings.append(encodings[0])
21:             known_names.append(name)
22:             print(f"已處理:{file}{name}")
23:     except UnidentifiedImageError:
24:         print(f"無法辨識的圖片:{file}")
25: 
26: # 儲存特徵
27: output_path = os.path.join(IMAGE_DIR, "encodings.pkl")
28: with open(output_path, "wb") as f:
29:     pickle.dump((known_encodings, known_names), f)
30: print(f"\n已儲存 {len(known_names)} 筆臉部特徵到 {output_path}")

7.5. 步驟三:即時人臉辨識(realtime_recognition.py)

 1: # realtime_recognition.py
 2: import pickle
 3: import cv2, face_recognition
 4: from ultralytics import YOLO
 5: 
 6: # ← 請改成你的 encodings.pkl 路徑
 7: with open("/Users/student/Desktop/images/encodings.pkl", "rb") as f:
 8:     known_encodings, known_names = pickle.load(f)
 9: 
10: model = YOLO("yolov8n.pt")
11: cap = cv2.VideoCapture(0)
12: 
13: while True:
14:     ret, frame = cap.read()
15:     if not ret:
16:         break
17:     results = model(frame, verbose=False)
18: 
19:     for box in results[0].boxes.xyxy:
20:         x1, y1, x2, y2 = map(int, box)
21:         face_img = frame[y1:y2, x1:x2]
22:         rgb_face = cv2.cvtColor(face_img, cv2.COLOR_BGR2RGB)
23: 
24:         encodings = face_recognition.face_encodings(rgb_face)
25:         if encodings:
26:             match = face_recognition.compare_faces(known_encodings, encodings[0])
27:             name = known_names[match.index(True)] if True in match else "Unknown"
28:             cv2.putText(frame, name, (x1, y1-10),
29:                         cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
30: 
31:     cv2.imshow("Face Recognition + YOLO", frame)
32:     if cv2.waitKey(1) & 0xFF == ord('q'):
33:         break
34: 
35: cap.release()
36: cv2.destroyAllWindows()

7.6. 中文姓名顯示

OpenCV 預設字型不支援中文。如果要顯示中文姓名(例如「王小明_1.jpg」),需要用 PIL 的字型:

 1: # realtime_recognition_zh.py
 2: from PIL import ImageFont, ImageDraw, Image
 3: import cv2, face_recognition, platform, pickle
 4: from ultralytics import YOLO
 5: import numpy as np
 6: 
 7: # 根據作業系統選擇中文字型
 8: system = platform.system()
 9: if system == "Windows":
10:     font_path = "C:/Windows/Fonts/msjh.ttc"
11: elif system == "Darwin":  # macOS
12:     font_path = "/System/Library/Fonts/STHeiti Medium.ttc"
13: else:  # Linux
14:     font_path = "/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc"
15: font = ImageFont.truetype(font_path, 32)
16: 
17: # ← 請改成你的路徑
18: with open("/Users/student/Desktop/images/encodings.pkl", "rb") as f:
19:     known_encodings, known_names = pickle.load(f)
20: 
21: model = YOLO("yolov8n.pt")
22: cap = cv2.VideoCapture(0)
23: 
24: while True:
25:     ret, frame = cap.read()
26:     if not ret:
27:         break
28:     results = model(frame, verbose=False)
29: 
30:     for box in results[0].boxes.xyxy:
31:         x1, y1, x2, y2 = map(int, box)
32:         face_img = frame[y1:y2, x1:x2]
33:         rgb_face = cv2.cvtColor(face_img, cv2.COLOR_BGR2RGB)
34: 
35:         encodings = face_recognition.face_encodings(rgb_face)
36:         if encodings:
37:             match = face_recognition.compare_faces(known_encodings, encodings[0])
38:             name = known_names[match.index(True)] if True in match else "未知"
39: 
40:             # 用 PIL 畫中文字
41:             frame_pil = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
42:             draw = ImageDraw.Draw(frame_pil)
43:             draw.text((x1, y1 - 40), name, font=font, fill=(0, 255, 0))
44:             frame = cv2.cvtColor(np.array(frame_pil), cv2.COLOR_RGB2BGR)
45: 
46:     cv2.imshow("Face Recognition + YOLO", frame)
47:     if cv2.waitKey(1) & 0xFF == ord('q'):
48:         break
49: 
50: cap.release()
51: cv2.destroyAllWindows()

7.7. YOLO 物件偵測 + 人臉辨識(合體版)

如果想同時辨識人臉(顯示姓名)和其他物件(貓、狗、車等),邏輯如下:

  1. 用 YOLO 偵測所有物件
  2. 如果偵測到 person(class_id=0),就用 face_recognition 進一步辨識是誰
  3. 其他物件正常顯示 YOLO 的類別標籤
 1: # yolo_face_combined.py
 2: import cv2, pickle, platform
 3: import face_recognition
 4: from ultralytics import YOLO
 5: from PIL import Image, ImageDraw, ImageFont
 6: import numpy as np
 7: 
 8: model = YOLO("yolov8n.pt")
 9: 
10: # ← 請改成你的路徑
11: with open("/Users/student/Desktop/images/encodings.pkl", "rb") as f:
12:     known_encodings, known_names = pickle.load(f)
13: 
14: system = platform.system()
15: if system == "Windows":
16:     font_path = "C:/Windows/Fonts/msjh.ttc"
17: elif system == "Darwin":
18:     font_path = "/System/Library/Fonts/STHeiti Medium.ttc"
19: else:
20:     font_path = "/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc"
21: font = ImageFont.truetype(font_path, 28)
22: 
23: cap = cv2.VideoCapture(0)
24: 
25: while True:
26:     ret, frame = cap.read()
27:     if not ret:
28:         break
29: 
30:     results = model(frame, verbose=False)
31:     boxes = results[0].boxes
32:     cls_names = results[0].names
33: 
34:     image_pil = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
35:     draw = ImageDraw.Draw(image_pil)
36: 
37:     for i, box in enumerate(boxes.xyxy):
38:         x1, y1, x2, y2 = map(int, box.tolist())
39:         cls_id = int(boxes.cls[i])
40: 
41:         if cls_id == 0:  # 是人 → 做人臉辨識
42:             face_img = frame[y1:y2, x1:x2]
43:             rgb_face = cv2.cvtColor(face_img, cv2.COLOR_BGR2RGB)
44:             encodings = face_recognition.face_encodings(rgb_face)
45: 
46:             name = "未知"
47:             if encodings:
48:                 matches = face_recognition.compare_faces(known_encodings, encodings[0])
49:                 if True in matches:
50:                     name = known_names[matches.index(True)]
51: 
52:             draw.rectangle([(x1, y1), (x2, y2)], outline="green", width=3)
53:             draw.text((x1, y1 - 30), name, font=font, fill=(0, 255, 0))
54:         else:  # 其他物件 → 顯示 YOLO 標籤
55:             label = cls_names[cls_id]
56:             draw.rectangle([(x1, y1), (x2, y2)], outline="blue", width=3)
57:             draw.text((x1, y1 - 30), label, font=font, fill=(0, 0, 255))
58: 
59:     frame = cv2.cvtColor(np.array(image_pil), cv2.COLOR_RGB2BGR)
60:     cv2.imshow("YOLO + Face Recognition", frame)
61:     if cv2.waitKey(1) & 0xFF == ord('q'):
62:         break
63: 
64: cap.release()
65: cv2.destroyAllWindows()

8. 訓練自己的 YOLO 模型

YOLO 預設能辨識 80 種物件,但如果你想辨識自訂物品(例如學生證、原子筆),就需要自己準備資料來訓練。

8.1. 設計流程

準備照片 → 標註物件位置 → 生成 data.yaml → 訓練模型 → 測試模型

8.2. 步驟一:建立資料夾結構

在你的專案資料夾下建立以下結構:

YOLO/
├── main.py
├── data.yaml
├── images/
│   ├── train/    ← 訓練用圖片(每種物件至少 10 張)
│   └── val/      ← 驗證用圖片(每種物件至少 5 張)
└── labels/
    ├── train/    ← 訓練圖片的標註檔(.txt)
    └── val/      ← 驗證圖片的標註檔(.txt)

8.3. 步驟二:準備圖片

為每種要辨識的物件拍攝照片,注意:

  • 不同角度、不同光線、不同背景
  • 命名規則:=物件名稱_編號.jpg=(例如 =CoffeeCup_1.jpg=)
  • 訓練和驗證要用*不同*的照片,否則會過擬合

8.4. 步驟三:標註物件位置

使用 makesense.ai 網站來標註每張圖片中物件的邊界框:

  1. 上傳圖片到 makesense.ai

    MakeSense-1.png

    Figure 1: makesense.ai 上傳圖片

  2. 加入 label 名稱,然後逐一標註每張圖片的物件邊界框
  3. 下載標註結果(選擇 YOLO 格式)

    MakeSense-3.png

    Figure 2: 以 YOLO 格式下載標註

  4. 分別標註 train 和 val 的圖片,把下載的 .txt 檔放入對應的 labels/trainlabels/val 資料夾

    MakeSense-4.png

    Figure 3: 完成後的資料夾結構

8.5. 步驟四:編輯 data.yaml

建立 data.yaml 設定檔,告訴 YOLO 你的資料在哪裡:

train: /Users/student/Desktop/YOLO/images/train
val: /Users/student/Desktop/YOLO/images/val
nc: 2
names: ['CoffeeCup', 'KeyChain']
  • nc: 物件種類數量
  • names: 所有的物件標籤名稱(就是你在 makesense.ai 中設定的 label)

8.6. 步驟五:訓練模型

在終端機中執行:

yolo detect train model=yolov8n.pt data=data.yaml epochs=50 imgsz=640

訓練完成後會看到類似以下輸出:

50 epochs completed in 0.15 hours.
Results saved to runs/detect/train/

訓練結果(包括模型權重)會存在 runs/detect/train/weights/ 資料夾中。

8.7. 步驟六:測試模型

 1: # test_custom_model.py
 2: import cv2, platform
 3: import numpy as np
 4: from PIL import Image, ImageDraw, ImageFont
 5: from ultralytics import YOLO
 6: 
 7: # ← 請改成你訓練出的模型路徑
 8: model = YOLO("runs/detect/train/weights/best.pt")
 9: 
10: # 中文字型
11: system = platform.system()
12: if system == "Windows":
13:     font_path = "C:/Windows/Fonts/msjh.ttc"
14: elif system == "Darwin":
15:     font_path = "/System/Library/Fonts/STHeiti Medium.ttc"
16: else:
17:     font_path = "/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc"
18: font = ImageFont.truetype(font_path, 28)
19: 
20: cap = cv2.VideoCapture(0)
21: cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
22: cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
23: 
24: while True:
25:     ret, frame = cap.read()
26:     if not ret:
27:         break
28: 
29:     results = model(frame, conf=0.1, verbose=False)
30: 
31:     image_pil = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
32:     draw = ImageDraw.Draw(image_pil)
33: 
34:     for box in results[0].boxes:
35:         cls_id = int(box.cls[0])
36:         conf = float(box.conf[0])
37:         label = f"{model.names[cls_id]} ({conf:.2f})"
38:         x1, y1, x2, y2 = map(int, box.xyxy[0])
39:         draw.rectangle([(x1, y1), (x2, y2)], outline=(255, 0, 0), width=3)
40:         draw.text((x1, y1 - 30), label, font=font, fill=(255, 0, 0))
41: 
42:     annotated = cv2.cvtColor(np.array(image_pil), cv2.COLOR_RGB2BGR)
43:     cv2.imshow("YOLOv8 Custom Detection", annotated)
44: 
45:     key = cv2.waitKey(1) & 0xFF
46:     if key == ord('q'):
47:         break
48:     elif key == ord('s'):
49:         cv2.imwrite("snapshot.jpg", frame)
50:         print("已儲存畫面 snapshot.jpg")
51: 
52: cap.release()
53: cv2.destroyAllWindows()

2025-05-01_13-51-13 (1).gif

Figure 4: YOLO 自訂物件偵測成果

8.8. 結合 YOLO 原本的辨識功能

如果想同時使用原本的 80 類偵測和你自訂的物件偵測,可以同時載入兩個模型:

 1: # dual_model_detection.py
 2: import cv2, platform
 3: import numpy as np
 4: from PIL import Image, ImageDraw, ImageFont
 5: from ultralytics import YOLO
 6: 
 7: # 兩個模型
 8: model_coco = YOLO("yolov8n.pt")       # 原生 80 類
 9: model_custom = YOLO("runs/detect/train/weights/best.pt")  # 自訓模型
10: 
11: system = platform.system()
12: if system == "Windows":
13:     font_path = "C:/Windows/Fonts/msjh.ttc"
14: elif system == "Darwin":
15:     font_path = "/System/Library/Fonts/STHeiti Medium.ttc"
16: else:
17:     font_path = "/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc"
18: font = ImageFont.truetype(font_path, 28)
19: 
20: cap = cv2.VideoCapture(0)
21: 
22: while True:
23:     ret, frame = cap.read()
24:     if not ret:
25:         break
26: 
27:     results_coco = model_coco(frame, conf=0.3, verbose=False)
28:     results_custom = model_custom(frame, conf=0.1, verbose=False)
29: 
30:     image_pil = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
31:     draw = ImageDraw.Draw(image_pil)
32: 
33:     # COCO 結果(藍框)
34:     for box in results_coco[0].boxes:
35:         cls_id = int(box.cls[0])
36:         conf = float(box.conf[0])
37:         label = f"{model_coco.names[cls_id]} ({conf:.2f})"
38:         x1, y1, x2, y2 = map(int, box.xyxy[0])
39:         draw.rectangle([(x1, y1), (x2, y2)], outline=(0, 0, 255), width=2)
40:         draw.text((x1, y1 - 30), label, font=font, fill=(0, 0, 255))
41: 
42:     # 自訓結果(紅框)
43:     for box in results_custom[0].boxes:
44:         cls_id = int(box.cls[0])
45:         conf = float(box.conf[0])
46:         label = f"{model_custom.names[cls_id]} ({conf:.2f})"
47:         x1, y1, x2, y2 = map(int, box.xyxy[0])
48:         draw.rectangle([(x1, y1), (x2, y2)], outline=(255, 0, 0), width=3)
49:         draw.text((x1, y1 - 30), label, font=font, fill=(255, 0, 0))
50: 
51:     annotated = cv2.cvtColor(np.array(image_pil), cv2.COLOR_RGB2BGR)
52:     cv2.imshow("COCO + Custom Model", annotated)
53:     if cv2.waitKey(1) & 0xFF == ord('q'):
54:         break
55: 
56: cap.release()
57: cv2.destroyAllWindows()

2025-05-01_14-25-13 (1).gif

Figure 5: YOLO 雙模型偵測成果

9. 進階:偵測後觸發動作

偵測到特定物件時,可以讓電腦做出反應(例如播放聲音、截圖等):

 1: # alert_detection.py
 2: import threading
 3: import subprocess
 4: import cv2
 5: from ultralytics import YOLO
 6: 
 7: model = YOLO("yolov8n.pt")
 8: 
 9: target_class = "person"
10: target_class_id = list(model.names.values()).index(target_class)
11: 
12: cap = cv2.VideoCapture(0)
13: 
14: def beep():
15:     """在背景執行播放聲音,不會卡住畫面"""
16:     subprocess.run(["afplay", "/System/Library/Sounds/Glass.aiff"])
17: 
18: while True:
19:     ret, frame = cap.read()
20:     if not ret:
21:         break
22: 
23:     results = model(frame, verbose=False)
24:     annotated = results[0].plot()
25: 
26:     class_ids = results[0].boxes.cls.cpu().numpy().astype(int) \
27:         if results[0].boxes.cls is not None else []
28: 
29:     # 偵測到目標就發出聲音
30:     if target_class_id in class_ids:
31:         threading.Thread(target=beep).start()
32: 
33:     cv2.imshow("YOLOv8 Alert Detection", annotated)
34:     if cv2.waitKey(1) & 0xFF == ord('q'):
35:         break
36: 
37: cap.release()
38: cv2.destroyAllWindows()

重點:用 threading.Thread 在背景播放聲音,不會卡住 webcam 畫面。

10. 擴充應用場景

應用 做法
辨識是誰 YOLO + face_recognition(需臉部特徵資料庫)
有沒有戴口罩 YOLO 自訓 or 搭配 CNN 分類器
穿什麼顏色衣服 OpenCV HSV 顏色過濾
偵測舉手、姿勢 YOLOv8-pose(支援骨架偵測)
自動點名系統 YOLO + face_recognition + 資料庫
門禁刷臉 YOLO + face_recognition + GPIO 控制
安全帽偵測 YOLO 自訓模型

11. 小結

概念 說明
物件偵測 找出圖片中所有物件的類別和位置
YOLO You Only Look Once,一次前向傳播完成所有偵測
YOLOv8 Ultralytics 開發,目前最常用的版本
邊界框 Bounding Box,用矩形框標出物件位置
COCO dataset 80 類物件的標準資料集
face_recognition 臉部辨識套件,可辨識「這個人是誰」
makesense.ai 線上標註工具,可標記物件邊界框
data.yaml 告訴 YOLO 資料集結構的設定檔

12. 作業

12.1. 作業一:YOLO 偵探日記

使用 YOLOv8 的預訓練模型偵測你身邊的物品。

步驟:

  1. 用手機拍攝 3 張不同場景的照片(例如:教室、家裡客廳、書桌)
  2. 用 YOLO 模型偵測每張照片中的物件
  3. 記錄偵測結果

你需要寫的程式碼(參考起始碼):

 1: from ultralytics import YOLO
 2: 
 3: model = YOLO("yolov8n.pt")
 4: results = model("your_photo.jpg")
 5: 
 6: # 印出偵測結果
 7: for box in results[0].boxes:
 8:     cls_id = int(box.cls[0])
 9:     conf = float(box.conf[0])
10:     print(f"  {model.names[cls_id]}: {conf:.1%}")
11: 
12: results[0].show()

預期輸出格式:

照片 1(教室):
  偵測到 5 個物件:
  - person: 95.2%
  - chair: 87.1%
  - laptop: 82.3%
  - keyboard: 76.4%
  - mouse: 68.9%

照片 2(客廳):
  偵測到 3 個物件:
  - couch: 91.0%
  - tv: 88.5%
  - remote: 45.2%  ← 信心度較低

照片 3(書桌):
  偵測到 4 個物件:...

分析:
  - 哪些物件被成功偵測?哪些漏掉了?
  - 信心度最低的是哪個物件?為什麼你覺得它信心度低?
  - YOLO 有沒有「認錯」的情況?

12.2. 作業二:打造組員辨識系統

完成人臉辨識系統的完整流程。

步驟:

  1. 每位組員拍 5~10 張不同角度的自拍照,命名為 姓名_n.jpg
  2. 執行 save_encodings.py 建立臉部特徵資料庫
  3. 執行 realtime_recognition.py 進行即時辨識
  4. 測試以下情境並記錄結果:
    • 正面對著攝影機
    • 側臉
    • 戴眼鏡 vs 不戴眼鏡
    • 光線明亮 vs 光線昏暗

預期輸出格式:

組員人數:4 人
照片總數:32 張

辨識測試結果:
| 情境         | 辨識成功率 |
|--------------+------------|
| 正面         | 4/4        |
| 側臉         | 2/4        |
| 戴眼鏡       | 3/4        |
| 光線昏暗     | 1/4        |

分析:
  - 哪些情況辨識率最高?為什麼?
  - 哪些情況辨識失敗?可能的原因?
  - 如果要改善辨識率,你覺得可以怎麼做?

12.3. 作業三:自訂 YOLO 物件偵測

訓練你自己的 YOLO 模型來偵測自訂物品。

基本要求:

  1. 可以即時辨識所有組員,並顯示組員姓名
  2. 可以即時辨識至少*三種物品*(其中必須包括學生證)

進階要求(加分):

  1. 當偵測到特定物品時,播放一段音效(提示:用 threading 避免畫面卡住)
  2. 當偵測到學生證時,自動截圖並儲存到桌面

繳交內容:

1. 訓練資料截圖(makesense.ai 標註畫面)
2. data.yaml 的內容
3. 訓練結果的 mAP50 數值(從訓練輸出中找到)
4. 即時偵測的成果截圖或錄影(至少包含 3 種自訂物品)
5. 程式碼(.py 檔案)

Footnotes:

Author: Yung-Chin Yen

Created: 2026-02-14 Sat 21:36