文、意如
一、什麼是正樣本?
定義:包含你要偵測的目標(如車牌)的圖片。
使用目的:讓分類器學習「車牌長什麼樣子」。
格式需求:圖片中要明確包含車牌。
當我們要訓練一個 Haar 特徵分類器來偵測特定目標(例如車牌)時,
第一步就是準備「正樣本」和「實測圖片」。正樣本是指那些明確包含目標的圖片,
訓練完成後會產生模擬可用實測的圖片(非訓練圖片)。
訓練完成後,我們會得到一個可以辨識車牌的分類器。
為了確認它真的有效,我們會用一些「不是訓練用的圖片」(實測圖片)來做測試。
這些測試圖片能幫助我們了解:
✅ 分類器能不能正確找出車牌?
✅ 在沒看過的新圖片上表現如何?
這樣可以確保訓練成果不只是「記住訓練圖片」,而是真的學會「辨識車牌」。
正樣本及實測圖片就是含有車牌號碼的圖片。
例如:

因此準備兩個資料夾
1.放置訓練用的圖片(收集大約 200~500 張就足以開始初步訓練)
2.放置實測用的圖片(建議準備 20~50 張圖片)
拍攝車牌的注意事項(正樣本收集時)
新舊車牌數量要均勻
為了讓分類器能學會辨識不同樣式的車牌,記得收集包含新式與舊式車牌的圖片,避免只訓練單一樣式,導致辨識偏差。
拍攝時手機與車牌要平行
盡量避免仰拍或斜拍,保持相機鏡頭與車牌平行,這樣可以減少角度變形,讓訓練資料更一致。
車牌大小適中
車牌在畫面中不能太小(難以辨識),也不能太大(無法提供背景參考)。理想狀況下,車牌應該佔整張圖片的**20%~50%**左右。
保持充足光線,避免強烈反光
拍攝時應該在白天或光線充足的環境下進行,避免過暗或逆光。同時要小心反光造成車牌數字無法辨識。
背景要多樣化
車牌背後的環境應包含不同場景(如:停車場、馬路邊、白天、傍晚等),這樣訓練出來的分類器才更具泛用性。
避免重複、模糊照片
模糊或重複角度的照片會影響訓練品質,拍攝完畢後應先篩選一遍,保留清晰、有用的影像。
轉換圖片尺寸
手機或圖片拍攝的圖片解析度非常大,不適合做為訓練用,必須轉換為較小圖片才能進行訓練。
範例會將圖片轉為300*225px(這個比例適合訓練 Haar 分類器等機器學習模型。)
有兩個資料夾都需要轉換(正樣本及實測圖片),因此將轉換程式寫成函式。
資料夾:
car_plate(放置訓練用的圖片100張)
car_plate_test(實測圖片8張)
car/car01.py
import PIL # 匯入 PIL 套件(Python Imaging Library)
from PIL import Image # 從 PIL 套件匯入 Image 模組,用來處理圖片
import glob # 匯入 glob 模組,用來抓取符合條件的檔案路徑
import shutil, os # 匯入 shutil 和 os 模組,用來處理檔案與資料夾
from time import sleep # 匯入 sleep 函數,用來暫停執行數秒(這裡用來等待刪除資料夾)
# 定義清空(刪除並重新建立)資料夾的函式
def emptydir(dirname):
if os.path.isdir(dirname): # 如果資料夾已存在
shutil.rmtree(dirname) # 刪除整個資料夾(含內容)
sleep(2) # 暫停 2 秒,確保系統完成刪除動作
os.mkdir(dirname) # 建立新的空資料夾
# 定義批次調整圖片大小的函式
def dirResize(src, dst):
myfiles = glob.glob(src + '/*.JPG') # 取得來源資料夾中所有 .JPG 圖片的路徑
emptydir(dst) # 先清空或建立目標資料夾
print(src + '資料夾:') # 顯示目前處理的來源資料夾
print('開始轉換圖形尺寸!') # 提示開始處理
for i, f in enumerate(myfiles): # 枚舉每一張圖片
img = Image.open(f) # 開啟圖片檔案
img_new = img.resize((300, 225), PIL.Image.ANTIALIAS) # 調整圖片大小為 300x225,使用抗鋸齒濾波提升畫質,補充 PIL.Image.ANTIALIAS 是一種高品質的縮圖過濾方式(目前新版改名為 Resampling.LANCZOS)。
outname = str("resizejpg") + str('{:0>3d}').format(i + 1) + '.jpg' # 組合新檔名(例如 resizejpg001.jpg)
img_new.save(dst + '/' + outname) # 儲存調整後的圖片至目標資料夾
print('轉換圖形尺寸完成 \n') # 顯示處理完成訊息
# 呼叫函式處理 'car_plate' 資料夾,輸出到 'car_resize_1'
dirResize('car_plate', 'car_resize_1')
# 呼叫函式處理 'car_plate_test' 資料夾,輸出到 'car_resize_2'
dirResize('car_plate_test', 'car_resize_2')
完成檔

正樣本圖片轉換為bmp格式
使用 OpenCV 工具 來訓練 Haar 特徵分類器時,官方建議正樣本圖片使用 .bmp
格式,原因如下:
無壓縮、無損品質:.bmp
是無壓縮格式,不會因壓縮導致特徵模糊,這對於 Haar 特徵(以亮暗區分)很重要。
OpenCV 工具相容性高:
在早期 OpenCV 版本中,.bmp
是最穩定支援的格式。雖然後來支援 .jpg
、.png
,但 .bmp
的穩定性仍較好。
那能不能用 .jpg
或 .png
?
可以,但請注意:
.jpg
是有損壓縮,有時會模糊特徵邊緣,不利訓練。
.png
雖然是無損,但透明通道(alpha channel)有時會導致讀取錯誤。
如果你使用 .jpg
,請確保畫質夠好且沒有過度壓縮。
實務建議
建議最終匯出的正樣本圖片轉存成 .bmp
(例如用 PIL 批次轉存)。
或使用時觀察是否報錯,如果沒問題也可使用 .jpg
。
轉檔程式碼:
car/car_02.py
from PIL import Image # 匯入 PIL 套件中的 Image 模組,用於影像處理
import glob # 匯入 glob 模組,用來找出符合特定樣式的檔案(如 *.JPG)
import os # 匯入 os 模組,用來操作檔案系統(如刪除檔案)
# 使用 glob 抓取 car_resize_1 資料夾中所有副檔名為 .JPG 的檔案
myfiles = glob.glob("car_resize_1/*.JPG")
print("開始轉換圖形格式") # 印出提示訊息
# 逐一處理每一個 JPG 檔案
for f in myfiles:
namespilt = f.split("\\") # 分割檔案路徑字串,取得檔名部分(Windows 路徑用 "\\")
img = Image.open(f) # 開啟圖片檔案
outname = namespilt[1].replace("resizejpg", "bmpraw") # 將檔名前綴從 resizejpg 改為 bmpraw
outname = outname.replace(".jpg", ".bmp") # 將副檔名從 .jpg 改為 .bmp
img.save("car_resize_1/" + outname, "bmp") # 儲存成 BMP 格式到相同資料夾
os.remove(f) # 刪除原本的 JPG 檔案,避免重複
print("轉換圖形格式結束") # 印出完成訊息
完成檔:

處理負樣本
在訓練 Haar 特徵分類器時,負樣本(Negative Samples)是指不包含目標物(例如:車牌)的圖片。它們的作用是幫助分類器學會「什麼不是目標」,進而減少誤判。
📌 補充說明:
尺寸略大於正樣本尺寸:這是為了讓分類器能從中「滑動視窗」並裁切出各種區塊進行學習,增加訓練的多樣性。
轉為灰階圖片:為了減少運算量與干擾,負樣本通常會轉成灰階。這樣分類器可以專注在亮度變化與特徵差異上,而不受顏色干擾。
例如,如果正樣本大小為 300x225
,則負樣本可以是 500x375
,並轉成灰階再使用。

原始檔:carNagative
轉成灰階檔後存成:carNagative1
程式碼:car/car_03.py
import PIL # 匯入 PIL 套件(Python Imaging Library),用於圖像處理
from PIL import Image # 從 PIL 套件中匯入 Image 模組,負責開啟、編輯、儲存圖片
import glob # 匯入 glob 模組,可用萬用字元搜尋檔案(例如 *.JPG)
import shutil, os # 匯入 shutil(處理檔案/資料夾操作)與 os(系統層級功能)模組
from time import sleep # 匯入 sleep 函數,用來讓程式暫停(例如等待刪除動作完成)
# 定義清空資料夾的函式(若存在則刪除後重建)
def emptydir(dirname):
if os.path.isdir(dirname): # 檢查目錄是否已存在
shutil.rmtree(dirname) # 如果存在,刪除整個資料夾(包含所有內容)
sleep(2) # 暫停 2 秒,確保系統完成刪除
os.mkdir(dirname) # 建立一個新的、空的資料夾
# 使用 glob 抓取 carNagative 資料夾下所有 .JPG 圖片路徑
myfiles = glob.glob("carNagative/*.JPG")
# 清空 carNagative 資料夾(注意:這會刪除所有原始檔案)
emptydir("carNagative1")
print("開始轉換尺寸及灰階") # 印出提示訊息
# 開始處理每一張圖片
for i, f in enumerate(myfiles): # 用 enumerate 同時取得索引 i 和檔案路徑 f
img = Image.open(f) # 開啟圖片
img_new = img.resize((500, 375), PIL.Image.LANCZOS) # 調整圖片尺寸為 500x375,使用高品質縮放算法 LANCZOS #補充 PIL.Image.ANTIALIAS 是一種高品質的縮圖過濾方式(目前新版改名為 Resampling.LANCZOS)。
img_new = img_new.convert('L') # 將圖片轉為灰階('L' 模式)
outname = str("negGray") + str("{:0>3d}").format(i+1) + ".jpg" # 組合新檔名,例如 negGray001.jpg
img_new.save("carNagative1/" + outname) # 儲存轉換後的圖片到 carNagative1 資料夾中
print("轉換尺寸及灰階完成")

Yiru@Studio - 關於我 - 意如