diff --git a/3_10_bg_subtraction.py b/3_10_bg_subtraction.py index 11f3e95..5c9d01e 100644 --- a/3_10_bg_subtraction.py +++ b/3_10_bg_subtraction.py @@ -1,5 +1,5 @@ # 実験課題10 -# 3.10 背景差分法(ラベリング・ノイズ処理含む) +# 3.10 背景差分法 import cv2 import numpy as np @@ -8,56 +8,42 @@ import numpy as np cap = cv2.VideoCapture(0) if not cap.isOpened(): - print("カメラを開けませんでした。") + print("カメラを開けませんでした.") else: - print("背景差分法を開始します。") - print("'q': 終了, 'r': 背景画像を現フレームで更新") - # 最初のフレームを背景画像として取得するための準備 - bg_gray = None + background = None while True: ret, frame = cap.read() if not ret: - print("フレームを取得できませんでした。") + print("フレームを取得できませんでした.") break - # 差分計算を安定させるため、現フレームをグレースケール化 + # 差分計算を安定させるため,現フレームをグレースケール化 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) - # ガウシアンブラ(平滑化)を適用して、カメラ特有の細かいノイズを低減 + # ガウシアンブラーを適用してカメラの細かいノイズを低減 gray = cv2.GaussianBlur(gray, (21, 21), 0) - # 起動直後、または 'r' キーが押された場合に背景画像を保存・更新する - if bg_gray is None: - bg_gray = gray - print("背景画像を保存しました。カメラの前から離れてください。") + if background is None: + background = gray continue - # --- 3.101 背景差分法:背景画像と現フレームの絶対差分を計算 --- - frame_diff = cv2.absdiff(bg_gray, gray) + # 背景画像と現フレームの絶対差分を計算 + frame_diff = cv2.absdiff(background, gray) - # 差分画像を2値化して、動いている領域(前景)を白(255)にする - # 閾値は環境に合わせて調整(ここでは例として 25) + # 差分画像を2値化して動いている領域を白(255)にする _, thresh = cv2.threshold(frame_diff, 25, 255, cv2.THRESH_BINARY) - # --- 3.102 ノイズ処理(モルフォロジー演算:オープニング) --- - # 3.7で学んだ手法を使い、背景に散らばる小さなチラツキノイズを消去 + # ノイズ処理 kernel = np.ones((5, 5), np.uint8) thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel) - # 領域内の穴を埋めて結合を強める(クロージング) thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel) - # --- 3.102 ラベリング処理 --- - # 2値化画像から、繋がっている白い領域(動体)を検出してラベルを貼る - # nlabels: ラベルの総数(背景含む) - # labels: 各画素にラベル番号(0, 1, 2...)が割り当てられた画像 - # stats: 各領域の矩形座標や面積[x, y, w, h, area]の行列 - # centroids: 各領域の中心座標 + # ラベリング処理 nlabels, labels, stats, centroids = cv2.connectedComponentsWithStats(thresh) - # 各ラベルごとに色分けするためのランダムな色マップを作成(背景の0は黒にする) - # 乱数のシードを固定して、フレーム間で色が激しく変わるのを防ぐ + # 各ラベルごとに色分けするためのランダムな色マップを作成 np.random.seed(42) colors = np.random.randint(0, 255, (nlabels, 3), dtype=np.uint8) colors[0] = [0, 0, 0] # ラベル0(背景)は黒に固定 @@ -65,33 +51,13 @@ else: # 画素ごとのラベル番号に対応する色を割り当てて、色分け画像(ラベリング画像)を作成 labeled_img = colors[labels] - # 動体領域に矩形(枠線)を描画する - for i in range(1, nlabels): # 0は背景なので1からループ - x = stats[i, cv2.CC_STAT_LEFT] - y = stats[i, cv2.CC_STAT_TOP] - w = stats[i, cv2.CC_STAT_WIDTH] - h = stats[i, cv2.CC_STAT_HEIGHT] - area = stats[i, cv2.CC_STAT_AREA] - - # 面積が小さすぎる領域(微小なノイズの残り)は無視する - if area > 500: - # 動体を緑色の矩形で囲む - cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2) - # ラベル番号を画像上に描画 - cv2.putText(frame, f"ID:{i}", (x, y - 10), - cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) - - # --- 結果の表示 --- cv2.imshow('Original Frame (Motion Detection)', frame) cv2.imshow('Foreground Mask (Thresh)', thresh) cv2.imshow('Labeled Components', labeled_img) - # キー入力の受付 - key = cv2.waitKey(1) & 0xFF - if key == ord('q'): + # 'q' キーが押されたら終了 + if cv2.waitKey(1) & 0xFF == ord('q'): break - elif key == ord('r'): - bg_gray = None # 次のループで背景が再キャプチャされる cap.release() cv2.destroyAllWindows() \ No newline at end of file diff --git a/3_11_tracking.py b/3_11_tracking.py index bbe32e0..03215c1 100644 --- a/3_11_tracking.py +++ b/3_11_tracking.py @@ -1,5 +1,5 @@ # 実験課題11 -# 3.11 トラッキング(任意) +# 3.11 トラッキング import cv2 @@ -7,62 +7,40 @@ import cv2 cap = cv2.VideoCapture(0) if not cap.isOpened(): - print("カメラを開けませんでした。") + print("カメラを開けませんでした.") else: - print("トラッキングテストを開始します。") # 最初の1フレームを読み込む ret, frame = cap.read() if not ret: - print("フレームを取得できませんでした。") + print("フレームを取得できませんでした.") else: - # --- 追跡対象の選択(ROI) --- - print("【操作方法】") - print("1. 追跡したい物体をマウスでドラッグして四角く囲んでください。") - print("2. 囲んだら Enter または Space キーを押して決定します。") - print("3. やり直す場合は 'c' キーを押してください。") - - # ユーザーに追跡対象をマウスで選択させるウィンドウを表示 - # bbox には選択された領域の (x, y, w, h) が格納される + # 追跡対象の選択 while True: # ユーザーに追跡対象をマウスで選択させる bbox = cv2.selectROI("Tracking", frame, fromCenter=False, showCrosshair=True) - # 幅(bbox[2]) または 高さ(bbox[3]) が 0 の場合 - # ('c'キーが押された、または未選択のままEnterが押された場合) + # 幅 または が 0 の場合 if bbox[2] == 0 or bbox[3] == 0: - print("領域がリセットされました。やり直します。") - print("もう一度マウスをドラッグして対象を囲み、Enterキーを押してください。") # continueによってループの先頭に戻り、再度selectROIを実行する continue else: # 正しく領域が選択されたらループを抜けてトラッキング処理へ進む - print("領域が確定しました。トラッキングを開始します。") break - # --- トラッカーの生成と初期化 --- - # 拡張モジュールに含まれる高精度なトラッカー「CSRT」を生成 - # ※OpenCVのバージョンによっては legacy に分類されるため、try-exceptで安全に呼び出す - try: - tracker = cv2.TrackerCSRT_create() - except AttributeError: - tracker = cv2.legacy.TrackerCSRT_create() + # トラッカーの生成 + tracker = cv2.TrackerCSRT_create() - # 最初のフレームと、選択した領域(bbox)を渡してトラッカーに学習(初期化)させる + # 最初のフレームと選択した領域を渡してトラッカーに学習させる tracker.init(frame, bbox) - print("追跡中... 終了するには 'q' キーを押してください。") - - # --- トラッキングのループ処理 --- + # トラッキングのループ処理 while True: ret, frame = cap.read() if not ret: break - - # 新しいフレームをトラッカーに渡し、対象がどこに移動したか推測させる - # success: 追跡成功(True) / 失敗(False) - # bbox: 新しい座標とサイズ (x, y, w, h) + # 新しいフレームをトラッカーに渡し,対象がどこに移動したか推測させる success, bbox = tracker.update(frame) if success: @@ -75,13 +53,11 @@ else: # 追跡失敗時(画面外に出た、何かの裏に完全に隠れた等):赤い警告文を表示 cv2.putText(frame, "Lost", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) - # 結果の表示 cv2.imshow("Tracking", frame) # 'q' キーで終了 if cv2.waitKey(1) & 0xFF == ord('q'): break - # 終了処理 cap.release() cv2.destroyAllWindows() \ No newline at end of file