YOLO11を使用した物体検出(動画)、CSVファイル作成（初心者向け）

## YOLOv11を用いた動画解析
一定間隔でのオブジェクト検出とデータ保存

#### 処理概要
- roboflowを利用してアノテーションデータを作成（別途記述）
- 保存済み動画のフレームを一定間隔ごとに取得
- YOLOv11でオブジェクト検出を実行
- 検出結果（バウンディングボックス座標・カテゴリなど）をCSVファイルに記録
- バウンディングボックスを描画した画像を保存
(画像ファイル名にCSVへの書き込み時間を付加し紐づけを行っています)

#### 作成環境
- Windows11 pro 16GBメモリ corei5 13GEN
- python 3.11.6
- ultlytics 8.3.53
- opencv 4.10.0.84
 
:::note info
プログラムはChatGPT4oを利用して作成しています。
（ミスも多いため手作業による修正は必須です）
:::

-----------------------------------------------------

#### 動画切り出し画像（jpg）
![ANA_Haneda_jpg_qiita.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3951823/b4584734-53af-495c-8236-979e4e4cbcf4.jpeg)

#### バウンディングボックズ情報（CSV）
![ANA_Haneda_csv_qiita.jpg.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3951823/675209c4-4394-4683-b181-31b53badbb54.png)

---------------------------------------------------
 #### プログラム（コメントはChatGPTによる自動作成） 
 ```python:movie_time_ver.py
from ultralytics import YOLO  # YOLOモデルを使用するためのライブラリ
from datetime import datetime, timedelta  # 現在時刻の取得や時間間隔の計算に使用
import cv2  # OpenCVライブラリ：画像や動画の処理
import csv  # CSVファイルの書き込み用ライブラリ
import os  # ファイル操作（存在確認・ディレクトリ作成など）
import time  # 時間制御用ライブラリ

# ================================
# 設定（ファイルパスなど）
# ================================
csv_filename = '' # 出力するCSVファイルのパス
model_path = ''  # YOLOモデルのパス
video_path = ''  # 入力動画のパス
output_image_dir = ''  # 処理後の画像を保存するディレクトリ

# ================================
# 必要なファイル・ディレクトリの確認と作成
# ================================
if not os.path.exists(model_path):
    raise FileNotFoundError(f"モデルファイルが見つかりません: {model_path}")

if not os.path.exists(video_path):
    raise FileNotFoundError(f"動画ファイルが見つかりません: {video_path}")

if not os.path.exists(output_image_dir):
    os.makedirs(output_image_dir)  # ディレクトリが存在しない場合は作成

# CSVファイル名（拡張子なし）を取得
csv_base_filename = os.path.splitext(os.path.basename(csv_filename))[0]    

# ================================
# CSVファイルの作成とヘッダーの書き込み
# ================================
header = ['date', 'No', 'Class', 'Label', 'Scores', 'id', 'x1', 'y1', 'x2', 'y2', 'time']
file = open(csv_filename, mode='w', newline='', encoding='utf-8')  # CSVファイルを新規作成
writer = csv.writer(file)  # CSV書き込みオブジェクトの作成
writer.writerow(header)  # ヘッダー情報をCSVに書き込む

# ================================
# YOLOモデルのロード
# ================================
model = YOLO(model_path)

# ================================
# 動画ファイルの読み込み
# ================================
cap = cv2.VideoCapture(video_path)  # 動画ファイルの読み込み

frame_cnt = 0  # フレームカウントの初期化
last_write_time = datetime.now() - timedelta(seconds=10)  # 最後の処理時間を初期化

# ================================
# 動画フレームの解析ループ
# ================================
while cap.isOpened():
    success, frame = cap.read()  # フレームを取得

    if success:
        frame_cnt += 1  # フレーム数をカウント
        cv2.imshow("YOLOv11 Inference", frame)  # 画面にフレームを表示

        # 一定間隔（2秒ごと）に解析処理を実行
        current_timestamp = datetime.now()
        if current_timestamp - last_write_time >= timedelta(seconds=2):
            # YOLOモデルを使ってフレームを解析
            results = model.track(frame, persist=True, conf=0.5, classes=[0, 2, 7])
            items = results[0]  # 検出結果を取得
            csv_rows = []  # 書き込むデータを格納するリスト
            annotated_frame = frame.copy()  # 画像描画用にフレームをコピー

            for item in items:
                # 現在のタイムスタンプを取得
                current_timestamp = datetime.now()
                date = current_timestamp.strftime('%Y/%m/%d')
                timestamp = current_timestamp.strftime('%H:%M:%S')
                timestamp_str = current_timestamp.strftime('%Y-%m-%d_%H-%M-%S')

                # 検出結果の各パラメータを取得
                cls = int(item.boxes.cls)  # クラスID
                label = item.names[int(cls)]  # クラス名
                score = item.boxes.conf.cpu().numpy()[0]  # 信頼スコア
                x1, y1, x2, y2 = item.boxes.xyxy.cpu().numpy()[0]  # バウンディングボックス座標

                # トラッキングIDの取得
                id_value = item.boxes.id
                track_ids = '' if id_value is None else item.boxes.id.int().cpu().tolist()[0]

                # CSVデータを作成
                csv_data = [date, str(frame_cnt), str(cls), str(label), str(score), str(track_ids),
                            str(x1), str(y1), str(x2), str(y2),  timestamp]
                csv_rows.append(csv_data)

                # バウンディングボックスの描画
                cv2.rectangle(annotated_frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2)
                
                # ラベル描画
                text = f"{label}: {score:.2f}" if track_ids else f"{label}: {score:.2f}"
                font_scale = 0.75 # フォントサイズ
                cv2.putText(annotated_frame, text, (int(x1), int(y1) - 10), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0, 255, 0), 2)

            writer.writerows(csv_rows)  # CSVに書き込み
            cv2.imwrite(os.path.join(output_image_dir, f"{csv_base_filename}_{timestamp_str}.jpg"), annotated_frame)  # 画像保存
            last_write_time = current_timestamp  # 最後の処理時間を更新

        if cv2.waitKey(1) & 0xFF == ord("q"):
            break
    else:
        break

cap.release()
cv2.destroyAllWindows()
file.close()

```
--------------------------------------------------------------
--------------------------------------------------------------
### 動画を解析するだけのプログラム（参考）

#### 解析保存動画のスクリーンショット
![スクリーンショット ANA_Haneda動画.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3951823/519ae1e5-07bf-4857-b085-78e0762556f8.png)


---------------------------------------------------
 #### プログラム（コメントはChatGPTによる自動作成） 
 ```python:movie_ver.py
import cv2
from ultralytics import YOLO

# ================================
# 設定（動画ファイルのパスなど）
# ================================
video_path = ''  # 処理対象の動画ファイル
model_path = ''  # YOLOモデルのパス
output_path = ''  # 出力動画の保存パス

# ================================
# YOLOモデルのロード
# ================================
model = YOLO(model_path)

# ================================
# 動画ファイルの読み込み
# ================================
cap = cv2.VideoCapture(video_path)

# 動画が正しく開けたか確認
if not cap.isOpened():
    print("Error: Could not open video.")
    exit()

# ================================
# 出力動画の設定
# ================================
fourcc = cv2.VideoWriter_fourcc(*'mp4v')  # 出力フォーマットの指定（MP4）
out = cv2.VideoWriter(output_path, fourcc, 30.0, (int(cap.get(3)), int(cap.get(4))))  # 動画ライターの設定

# ================================
# 動画フレームの解析ループ
# ================================
while(cap.isOpened()):
    ret, frame = cap.read()  # フレームを取得
    if ret:
        # YOLOv8による物体検出を実行
        results = model.predict(frame, conf=0.5, iou=0.45, device='cpu', verbose=False)
        # conf: 信頼度閾値（0.5）
        # iou: IoU閾値（0.45）
        # device: 'cpu' または 'cuda'（GPU使用時は'cuda'）
        # verbose: 詳細ログの抑制
        
        # 検知結果を描画
        annotated_frame = results[0].plot()
        
        # 出力動画にフレームを書き込む
        out.write(annotated_frame)
        
        # フレームを表示
        cv2.imshow('Frame', annotated_frame)
        
        # 'q'キーが押されたら処理を終了
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    else:
        break

# ================================
# リソースの解放
# ================================
cap.release()
out.release()
cv2.destroyAllWindows()

```
--------------------------------------------------------------

機械学習、roboflow利用方法に関しては後日作成します。
![roboflow_qiita.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3951823/6f68587c-b755-4290-8872-b4d57c24487e.png)
[roboflow](https://roboflow.com)

--------------------------------------------------------------
---------------------------------------------------------------

画像、引用
[ANA](https://www.ana.co.jp)
[HANEDA,Tokyo International Airport Terminal2【LIVE】ANN/テレ朝](https://www.youtube.com/watch?v=A0FCKcTuRHo)
[Ultralytics](https://docs.ultralytics.com/ja/modes/)
[yolov8を用いてmp4動画ファイルの物体検知をする](https://zenn.dev/headwaters/articles/b28114b2ff4b86)