交通紅綠燈辨識是一個經典的影像處理應用案例,無論是機器人導航、車輛輔助駕駛,甚至影像監控系統,都少不了這個功能。
今天我們要用 OpenCV 和 NumPy,搭配 HSV 色彩空間,快速實作一個 高穩定度的紅綠燈判斷系統。
結果圖

1. 為什麼要用 HSV 而不是 RGB
傳統 RGB 判斷顏色的缺點:- 光線強度改變時,RGB 數值波動大
- 同樣的紅燈,白天和夜晚拍攝,RGB 值差異極大
- 需要人工設定多個條件,程式複雜
HSV 的優勢:
- H (Hue 色相):直接代表顏色的種類(紅、綠、藍…)
- S (Saturation 飽和度):顏色的純度
- V (Value 亮度):顏色的明暗度
因此,用 HSV 只需要判斷色相 (H) 就能輕鬆穩定地抓到紅燈或綠燈。
2. 安裝必要套件
在開始前,先安裝以下套件:
pip install opencv-python numpy matplotlib
3. 完整程式碼
以下是完整、可直接執行的紅綠燈偵測程式:
import cv2
import numpy as np
import matplotlib.pyplot as plt
def detect_traffic_light_hsv(img):
# Step 1. 轉換色彩空間 BGR → HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# Step 2. 定義紅色範圍 (紅色分兩段範圍)
lower_red1, upper_red1 = np.array([0, 70, 50]), np.array([10, 255, 255])
lower_red2, upper_red2 = np.array([170, 70, 50]), np.array([180, 255, 255])
# Step 3. 定義綠色範圍
lower_green, upper_green = np.array([40, 100, 100]), np.array([90, 255, 255])
# Step 4. 建立紅色與綠色的遮罩
mask_red = cv2.inRange(hsv, lower_red1, upper_red1) | cv2.inRange(hsv, lower_red2, upper_red2)
mask_green = cv2.inRange(hsv, lower_green, upper_green)
# Step 5. 計算紅色與綠色的比例
red_rate = np.sum(mask_red > 0) / mask_red.size
green_rate = np.sum(mask_green > 0) / mask_green.size
# Step 6. 視覺化結果
fig, axes = plt.subplots(1, 3, figsize=(20, 6))
axes[0].set_title(f"Red Mask: {red_rate:.2%}")
axes[0].imshow(mask_red, cmap='gray')
axes[0].axis("off")
axes[1].set_title(f"Green Mask: {green_rate:.2%}")
axes[1].imshow(mask_green, cmap='gray')
axes[1].axis("off")
axes[2].set_title("Original Image")
axes[2].imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
axes[2].axis("off")
plt.show()
# Step 7. 判斷結果
if red_rate > green_rate and red_rate > 0.01:
return "🔴 紅燈"
elif green_rate > red_rate and green_rate > 0.01:
return "🟢 綠燈"
else:
return "⚪ 未偵測到紅或綠燈"
4. 程式邏輯解析
(1) 轉換到 HSV 色彩空間
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
OpenCV 讀入影像是 BGR,要先轉成 HSV。
(2) 設定顏色範圍
lower_red1, upper_red1 = np.array([0, 70, 50]), np.array([10, 255, 255])
lower_red2, upper_red2 = np.array([170, 70, 50]), np.array([180, 255, 255])
lower_green, upper_green = np.array([40, 70, 50]), np.array([90, 255, 255])
- 紅色因為 Hue 角度是 0° 或 180° 附近,需要分成兩段判斷。
- 綠色大約落在 40°~90° 的範圍。
(3) 計算紅/綠像素比例
red_rate = np.sum(mask_red > 0) / mask_red.size
green_rate = np.sum(mask_green > 0) / mask_green.size
統計有多少像素落在紅色或綠色的範圍,比例越高,代表該顏色燈號越亮。
(4) 簡單判斷邏輯
if red_rate > green_rate and red_rate > 0.01:
return "🔴 紅燈"
elif green_rate > red_rate and green_rate > 0.01:
return "🟢 綠燈"
else:
return "⚪ 未偵測到紅或綠燈"
設定
0.01
當作亮度比例的下限,避免雜訊造成誤判。
5. 如何測試
假設有一張紅綠燈照片 traffic_light.jpg
:
img = cv2.imread("traffic_light.jpg")
result = detect_traffic_light_hsv(img)
print(result)
執行後,終端輸出:
🟢 綠燈
同時彈出視覺化視窗:
- 左圖 → 紅色遮罩
- 中圖 → 綠色遮罩
- 右圖 → 原圖
6. 進階技巧
(1) 限制 ROI (Region of Interest)
若紅綠燈位置固定,可以只分析該區域:
roi = img[50:200, 100:300]
detect_traffic_light_hsv(roi)
(2) 加入模糊處理降低雜訊
blur = cv2.GaussianBlur(img, (5, 5), 0)
讓燈光更集中,提升辨識穩定性。