Python | OpenCV 資料蒐集
📚 前言 在上一篇 模型訓練與微調 中,我們了解了整個訓練流程的概觀。 訓練的第一步,也是最重要的基礎,就是 資料蒐集 (Data Collection) 。
「垃圾進,垃圾出 (Garbage in, garbage out)」是機器學習不變的定律。 資料的數量與品質,直接決定了模型的上限。
❓ 需要多少資料? 資料量並沒有固定標準,但以下是常見的經驗法則:
任務類型
建議最低資料量(每類別)
圖片分類
100 ~ 500 張
物件偵測
200 ~ 1000 張(含標註)
語義分割
500 張以上
遷移學習微調
50 ~ 200 張即可有效果
💡 資料多樣性比數量更重要:涵蓋不同光線、角度、背景、遮擋情況。
🗂️ 資料蒐集方法 方法一:公開資料集 最快速且品質穩定的方式,適合快速驗證想法或作為預訓練基礎。
方法二:使用 Python 批次下載圖片 可透過 icrawler 套件從 Bing 批次下載圖片。
⚠️ GoogleImageCrawler 因 Google 改版目前已無法使用,請改用 BingImageCrawler。
1 2 3 4 5 from icrawler.builtin import BingImageCrawlercrawler = BingImageCrawler(storage={"root_dir" : "dataset/cat" }) crawler.crawl(keyword="cat" , max_num=200 )
圖:使用 BingImageCrawler 批次下載圖片至本地資料夾
⚠️ 注意版權問題,建議使用 Creative Commons 授權圖片,或僅用於研究與學習目的。
方法三:使用 OpenCV 自行拍攝 透過攝影機即時拍攝,按空白鍵存圖、按 q 離開。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import cv2import ossave_dir = "dataset/custom" os.makedirs(save_dir, exist_ok=True ) cap = cv2.VideoCapture(0 ) count = 0 print("按下空白鍵拍照,按 q 離開" ) while True : ret, frame = cap.read() if not ret: break cv2.imshow("Capture" , frame) key = cv2.waitKey(1 ) & 0xFF if key == ord(' ' ): filename = os.path.join(save_dir, f"{count:04 d} .jpg" ) cv2.imwrite(filename, frame) print(f"已儲存:{filename} " ) count += 1 elif key == ord('q' ): break cap.release() cv2.destroyAllWindows()
圖:按空白鍵從攝影機拍照並儲存至資料集
註:由於本身沒有攝影鏡頭,所以無法示範效果。
方法四:從影片中截取影格 若已有錄製好的影片,可自動截取每隔 N 幀的影像。
範例影片:
載點:video.zip
來源:此影片取自 Pexels ,屬於無版權影片,可自由下載與使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import cv2import osvideo_path = "video.mp4" save_dir = "dataset/frames" os.makedirs(save_dir, exist_ok=True ) cap = cv2.VideoCapture(video_path) frame_interval = 10 count = 0 saved = 0 while True : ret, frame = cap.read() if not ret: break if count % frame_interval == 0 : filename = os.path.join(save_dir, f"{saved:04 d} .jpg" ) cv2.imwrite(filename, frame) saved += 1 count += 1 cap.release() print(f"共截取 {saved} 張影格" )
圖:從影片中每隔 N 幀截取一張影格並儲存
🎬 實戰範例:從影片自動建立高品質訓練資料集 上面方法四的腳本有兩個實際問題:模糊幀(動態模糊導致影像無法使用)和重複幀(相機靜止時連續影格幾乎一樣,徒增資料量)。以下腳本自動過濾這兩類問題。
💡 執行前建議 :不同影片的壓縮程度差異很大,先執行 diagnose_video.py 確認影片的 Laplacian 值範圍,再決定 BLUR_THRESHOLD。手機直拍通常在 50 ~ 200 之間。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import cv2import numpy as npVIDEO_PATH = "toy_car.mp4" cap = cv2.VideoCapture(VIDEO_PATH) fps = cap.get(cv2.CAP_PROP_FPS) or 30.0 step = max(1 , int(fps * 0.5 )) values = [] frame_idx = 0 while True : ret, frame = cap.read() if not ret: break if frame_idx % step == 0 : gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) values.append(cv2.Laplacian(gray, cv2.CV_64F).var()) frame_idx += 1 cap.release() values.sort() print(f"取樣幀數:{len(values)} " ) print(f"最小值:{min(values):.1 f} 最大值:{max(values):.1 f} " ) print(f"中位數:{np.median(values):.1 f} 平均值:{np.mean(values):.1 f} " ) print(f"建議 BLUR_THRESHOLD:{np.median(values) * 0.5 :.1 f} " )
圖:diagnose_video.py 輸出影片幀的 Laplacian 值統計與建議閾值
確認數值後,將結果填入下方腳本的 BLUR_THRESHOLD。
拍攝情境與參數建議:
拍攝方式
INTERVAL_SEC
BLUR_THRESHOLD
HASH_THRESHOLD
手持繞物行走(鏡頭移動)
0.5
依診斷值
5 ~ 8
轉盤旋轉 + 固定鏡頭
1.0
依診斷值
1
行車記錄器或街景
0.3
依診斷值
8 ~ 10
💡 轉盤拍攝注意事項 :轉盤拍攝的背景完全靜止,pHash 對整張圖計算,背景佔大部分畫面,導致每幀 hash 非常相近,幾乎全被判成重複。建議將 HASH_THRESHOLD 設為 1(僅過濾完全相同的幀),改用 INTERVAL_SEC 控制取樣密度即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 import cv2import osimport numpy as npVIDEO_PATH = "toy_car.mp4" SAVE_DIR = "dataset/toy_car" INTERVAL_SEC = 0.5 BLUR_THRESHOLD = 21.6 HASH_THRESHOLD = 1 os.makedirs(SAVE_DIR, exist_ok=True ) def is_blurry (frame, threshold) : gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) return cv2.Laplacian(gray, cv2.CV_64F).var() < threshold def phash (frame) : gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) small = cv2.resize(gray, (8 , 8 ), interpolation=cv2.INTER_AREA) bits = (small > small.mean()).flatten() return "" .join("1" if b else "0" for b in bits) def hamming (a, b) : return sum(x != y for x, y in zip(a, b)) def collect (video_path, save_dir, interval_sec, blur_thresh, hash_thresh) : cap = cv2.VideoCapture(video_path) if not cap.isOpened(): print(f"❌ 無法開啟影片:{video_path} " ) return fps = cap.get(cv2.CAP_PROP_FPS) or 30.0 total_f = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) step = max(1 , int(fps * interval_sec)) print(f"影片資訊:{total_f} 幀 / {fps:.1 f} fps / {total_f/fps:.1 f} s" ) print(f"模糊閾值:{blur_thresh} | 重複閾值:< {hash_thresh} " ) print("-" * 50 ) saved, skip_blur, skip_dup = 0 , 0 , 0 frame_idx = 0 hash_pool = [] while True : ret, frame = cap.read() if not ret: break if frame_idx % step == 0 : if is_blurry(frame, blur_thresh): skip_blur += 1 else : h = phash(frame) if any(hamming(h, ph) < hash_thresh for ph in hash_pool): skip_dup += 1 else : fname = os.path.join(save_dir, f"{saved:05 d} .jpg" ) cv2.imwrite(fname, frame, [cv2.IMWRITE_JPEG_QUALITY, 95 ]) hash_pool.append(h) saved += 1 frame_idx += 1 if frame_idx % 100 == 0 : pct = frame_idx / total_f * 100 print(f"\r進度:{pct:5.1 f} % 已儲存:{saved} " f"跳過(模糊):{skip_blur} 跳過(重複):{skip_dup} " , end="" , flush=True ) cap.release() print(f"\n{'=' * 50 } " ) print(f"完成!輸出目錄:{save_dir} " ) print(f" ✅ 儲存:{saved} 張" ) print(f" 🌀 跳過(模糊):{skip_blur} 張" ) print(f" 📋 跳過(重複):{skip_dup} 張" ) if __name__ == "__main__" : collect(VIDEO_PATH, SAVE_DIR, INTERVAL_SEC, BLUR_THRESHOLD, HASH_THRESHOLD)
圖:collect_from_video.py 執行輸出,顯示進度與過濾統計
🔍 資料品質檢查 蒐集完後,建議先掃描是否有損壞的圖片:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import cv2import osdef check_images (directory) : broken = [] for root, _, files in os.walk(directory): for f in files: if f.lower().endswith(('.jpg' , '.jpeg' , '.png' )): path = os.path.join(root, f) if cv2.imread(path) is None : broken.append(path) return broken broken_files = check_images("dataset" ) print(f"損壞圖片數量:{len(broken_files)} " ) for f in broken_files: print(f)
圖:check_images.py 掃描資料集,輸出損壞圖片清單
⚠️ 注意事項
版權問題 :商業用途需確認授權,避免使用有版權限制的圖片。
資料隱私 :若涉及人臉或個人資訊,需遵守當地法規(如 GDPR)。
訓練/驗證集分離 :兩者資料絕不能重疊。
類別均衡 :若某類別資料太少,可透過資料增強補足。
🎯 結語 資料蒐集看似簡單,但實際上是整個訓練流程中最影響結果的環節之一。 花時間蒐集高品質、多樣性的資料,後續的訓練才能事半功倍。
下一步是為資料進行 標註 (Annotation) ,為每張圖片建立模型學習所需的正確答案。
📖 如在學習過程中遇到疑問,或是想了解更多相關主題,建議回顧一下 Python | OpenCV 系列導讀 ,掌握完整的章節目錄,方便快速找到你需要的內容。
註:以上參考了OpenCV 官方文件 — Tutorials COCO Dataset Roboflow Universe icrawler GitHub