RF-DETRによる物体検出・BoT-SORTによる追跡とBoT-SORTの可視化,TTAの機能付き(COCO 80クラス)(ソースコードと説明と利用ガイド)

概要

RF-DETRを用いた物体検出システムで、動画やウェブカメラからCOCO 80クラスの物体をリアルタイムで検出する。CLAHE前処理とTTAにより暗所でも高精度な検出が可能。BoT-SORTによる物体追跡機能を搭載し、フレーム間での継続的な追跡を実現する。

その他情報

【概要説明】 [PDF], [パワーポイント]

RF-DETR物体検出 RF-DETR物体検出

目次

第1章 プログラム利用ガイド

色分けによる検出の可視化

画面表示の改善

設定値

可視化により、BoT-SORTがどのように動作しているかが明確に分かります:

プログラムを実行すると、これらの色分けがリアルタイムで確認できます。

第2章 事前準備

ここでは、最低限の事前準備について説明する。機械学習や深層学習を行う場合は、NVIDIA CUDA、Visual Studio、Cursorなどを追加でインストールすると便利である。これらについては別ページ https://www.kkaneko.jp/cc/dev/aiassist.htmlで詳しく解説しているので、必要に応じて参照してください。

第3章 Python 3.12 のインストール

Pythonのインストールを行い、Pythonのプログラムを実行する環境を整える。扱う環境は、Windows搭載パソコンである。金子研究室では、Python 3.12.10を推奨する。

[Windows での Python 3.12 のインストール手順を見るには、ここをクリック]

Windows での Python 3.12 のインストール

以下のいずれかの方法でPython 3.12をインストールする。Pythonがインストール済みの場合、この手順は不要である。

方法 1:winget によるインストール

インストールコマンドの実行方法

管理者権限コマンドプロンプトを起動する(手順:Windowsキーまたはスタートメニュー → cmd と入力 → 右クリック → 「管理者として実行」)。そして、コマンド全体をコマンドプロンプトにコピー&ペーストする。

--scope machine を指定することで、システム全体(全ユーザー向け)にインストールされる。このオプションの実行には管理者権限が必要である。インストール完了後、コマンドプロンプトを再起動するとPATHが反映される。

REM Python 3.12 をシステム領域にインストール
winget install --id Python.Python.3.12 -e --scope machine --silent --accept-source-agreements --accept-package-agreements --override "/quiet InstallAllUsers=1 PrependPath=1 Include_test=0 Include_pip=1 Include_launcher=1 InstallLauncherAllUsers=1 TargetDir=\"C:\Program Files\Python312\""

REM Python と Scripts を PATH 先頭に追加
powershell -NoProfile -Command "$p='C:\Program Files\Python312'; $s=\"$p\Scripts\"; $c=[Environment]::GetEnvironmentVariable('Path','Machine'); if((Test-Path $p) -and (';'+$c+';' -notlike \"*;$p;*\") -and (';'+$c+';' -notlike \"*;$s;*\")){[Environment]::SetEnvironmentVariable('Path',\"$p;$s;$c\",'Machine')}"

方法 2:インストーラーによるインストール

  1. Python公式サイト(https://www.python.org/downloads/)にアクセスし、「Download Python 3.x.x」ボタンからWindows用インストーラーをダウンロードする。
  2. ダウンロードしたインストーラーを実行する。
  3. 初期画面の下部に表示される「Add python.exe to PATH」にチェックを入れてから「Customize installation」を選択する。このチェックを入れ忘れると、コマンドプロンプトから python コマンドを実行できない。
  4. 「Install Python 3.xx for all users」にチェックを入れ、「Install」をクリックする。

インストールの確認

コマンドプロンプトで以下を実行する。

python --version

バージョン番号(例:Python 3.12.x)が表示されればインストール成功である。「'python' は、内部コマンドまたは外部コマンドとして認識されていません。」と表示される場合は、インストールが正常に完了していない。

第4章 Python の開発環境 Visual Studio Code のインストールと Python 用の設定

Python の開発環境Visual Studio Code(プログラムを編集するソフトウェア。以下、VS Code)を整える。

[Windows での Visual Studio Code のインストールと Python 用の設定手順を見るには、ここをクリック]

Windows での Visual Studio Code のインストールと Python 用の設定手順

1. VS Code と拡張機能のインストール

以下のコマンドにより,既存の VS Code を削除し,全ユーザー共有の設定で再インストールしたうえで,拡張機能(VS Code に機能を追加するソフトウェア)をまとめて導入する.

インストールコマンドの実行方法

管理者権限コマンドプロンプトを起動する(手順:Windows キーまたはスタートメニュー → cmd と入力 → 右クリック → 「管理者として実行」)。そして,コマンド全体をコマンドプロンプトにコピー&ペーストする。

インストールコマンド


REM ============================================================
REM Microsoft Visual Studio Code
REM ============================================================
winget uninstall -e --id Microsoft.VisualStudioCode --silent --disable-interactivity --accept-source-agreements
rmdir /s /q C:\ProgramData\vscode-extensions 2>nul
rmdir /s /q "%APPDATA%\Code" 2>nul
rmdir /s /q "%USERPROFILE%\.vscode" 2>nul
rmdir /s /q "%LOCALAPPDATA%\Microsoft\vscode-update" 2>nul

REM VS Code をシステム領域に新規インストール
winget install --scope machine --id Microsoft.VisualStudioCode -e --silent --accept-source-agreements --accept-package-agreements

REM 全ユーザー共有の拡張機能フォルダ
mkdir C:\ProgramData\vscode-extensions 2>nul
icacls "C:\ProgramData\vscode-extensions" /grant "Everyone:(OI)(CI)M" /T

REM スタートメニューのショートカットを --extensions-dir 付きで再作成
rmdir /s /q "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Visual Studio Code" 2>nul
del "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Visual Studio Code.lnk" 2>nul
powershell -NoProfile -Command "$s=New-Object -ComObject WScript.Shell; $lnk=$s.CreateShortcut('C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Visual Studio Code.lnk'); $lnk.TargetPath='C:\Program Files\Microsoft VS Code\Code.exe'; $lnk.Arguments='--extensions-dir \"C:\ProgramData\vscode-extensions\"'; $lnk.Save()"
REM ショートカットの検証
powershell -NoProfile -Command "$s=New-Object -ComObject WScript.Shell; $lnk=$s.CreateShortcut('C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Visual Studio Code.lnk'); Write-Host 'TargetPath:' $lnk.TargetPath; Write-Host 'Arguments:' $lnk.Arguments"

REM ファイル / フォルダ右クリックの「Code で開く」を登録
reg add "HKLM\SOFTWARE\Classes\*\shell\VSCode\command" /ve /d "\"C:\Program Files\Microsoft VS Code\Code.exe\" --extensions-dir \"C:\ProgramData\vscode-extensions\" \"%1\"" /f
reg add "HKLM\SOFTWARE\Classes\Directory\shell\VSCode\command" /ve /d "\"C:\Program Files\Microsoft VS Code\Code.exe\" --extensions-dir \"C:\ProgramData\vscode-extensions\" \"%1\"" /f
reg add "HKLM\SOFTWARE\Classes\Directory\Background\shell\VSCode\command" /ve /d "\"C:\Program Files\Microsoft VS Code\Code.exe\" --extensions-dir \"C:\ProgramData\vscode-extensions\" \"%V\"" /f

REM --extensions-dir 付きで起動する code.cmd ラッパを作成
REM (%* を echo で書くと対話的 cmd で失われるため、PowerShell で [char]37+'*' を書き出す)
powershell -NoProfile -Command "$pct=[char]37; $q=[char]34; $c='@echo off'+[char]13+[char]10+$q+'C:\Program Files\Microsoft VS Code\bin\code.cmd'+$q+' --extensions-dir '+$q+'C:\ProgramData\vscode-extensions'+$q+' '+$pct+'*'+[char]13+[char]10; [IO.File]::WriteAllText('C:\ProgramData\vscode-extensions\vscode.cmd',$c,[Text.Encoding]::ASCII)"

REM 拡張機能のインストール
set "CODE=C:\Program Files\Microsoft VS Code\bin\code.cmd"
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --uninstall-extension GitHub.copilot
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --uninstall-extension GitHub.copilot-chat
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension ms-python.python
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension ms-python.vscode-pylance
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension ms-python.debugpy
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension MS-CEINTL.vscode-language-pack-ja
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension saoudrizwan.claude-dev
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension rust-lang.rust-analyzer
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension tamasfe.even-better-toml
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension anthropic.claude-code
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension almenon.arepl
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --list-extensions --show-versions
echo === セットアップ完了 ===

2. Python インタプリタの選択

同一マシンに複数の Python がインストールされている場合,VS Code で使用する Python 本体(インタプリタ:Python プログラムを解釈・実行するソフトウェア)を選択する必要がある.

  1. コマンドパレット(コマンド名で機能を呼び出す VS Code の入力欄)を開く(Ctrl+Shift+P
  2. Python: Select Interpreter と入力する
  3. 表示される一覧から,使用する Python(例:C:\Program Files\Python312\python.exe)を選択する.

必要なパッケージのインストール

管理者権限コマンドプロンプトを起動する (手順:Windowsキーまたはスタートメニュー → cmd と入力 → 右クリック → 「管理者として実行」)。

REM PyTorch をインストール(GPU対応版)
set "CUDA_TAG=cu128"
set "PYTHON_PATH=C:\Program Files\Python312"
"%PYTHON_PATH%\Scripts\pip" install --no-user -U numpy torch torchvision torchaudio --index-url https://download.pytorch.org/whl/%CUDA_TAG%
pip install --no-user -U rfdetr opencv-python numpy pillow boxmot supervision

第5章 RF-DETRによる物体検出プログラム・BoT-SORTによる追跡とTTAの機能付き(COCO 80クラス)

概要

このプログラムは、RF-DETRを用いた物体検出システムである。動画ファイル、ウェブカメラ、サンプル動画から取得した映像に対してリアルタイムで物体検出を実行し、COCOデータセット80クラスの物体をバウンディングボックスで表示する。検出精度の向上を目的として、CLAHE(コントラスト制限付き適応ヒストグラム均一化)とTTA(Test-Time Augmentation)を組み合わせた前処理を実装している[1][2]。

主要技術

RF-DETR(Roboflow Detection Transformer)

Roboflowが開発したTransformerベースのリアルタイム物体検出モデルである[1][2]。DINOv2による自己教師あり事前学習済みバックボーンと、Deformable DETR・LW-DETRの設計を組み合わせ、COCOデータセットで60 mAPを超える初のリアルタイム物体検出モデルとなった。NMS(非最大抑制)を必要としないエンドツーエンドのアーキテクチャを採用し、RT-DETRv2を含む既存のリアルタイム検出器より高い精度を達成する。Apache 2.0ライセンスで提供される。

CLAHE(Contrast Limited Adaptive Histogram Equalization)

Zuiderveldが1994年に提案したコントラスト強化手法である[3][4]。画像を小領域(タイル)に分割し、各タイルでヒストグラム均一化を適用する。コントラスト制限機能により、ノイズの過度な増幅を防止する。

BoT-SORT

カルマンフィルタとカメラモーション補正を組み合わせた物体追跡手法である[7]。低信頼度検出も含めた2段階の関連付けに加え、外観特徴(ReID)によるマッチングをオプションで有効化でき、遮蔽環境でもByteTrackより安定した追跡を実現する。

技術的特徴

実装の特色

リアルタイム映像処理に特化した設計を採用し、以下の機能を備える:

参考文献

[1] Robinson, I., Robicheaux, P., & Popov, M. (2025). RF-DETR: SOTA Real-Time Object Detection Model. Roboflow. https://github.com/roboflow/rf-detr

[2] Roboflow. (2026). RF-DETR Documentation. https://rfdetr.roboflow.com/

[3] Zuiderveld, K. (1994). Contrast limited adaptive histogram equalization. Graphics gems IV, 474-485.

[4] OpenCV Team. (2024). Histogram Equalization Documentation. https://docs.opencv.org/4.x/d5/daf/tutorial_py_histogram_equalization.html

[5] Shanmugam, D., Blalock, D., Balakrishnan, G., & Guttag, J. (2021). When and why test-time augmentation works. arXiv preprint arXiv:2011.11156.

[6] Machine Learning Mastery. (2020). How to Use Test-Time Augmentation. https://machinelearningmastery.com/how-to-use-test-time-augmentation-to-improve-model-performance-for-image-classification/

[7] Aharon, N., Orfaig, R., & Bobrovsky, B. (2022). BoT-SORT: Robust Associations Multi-Pedestrian Tracking. arXiv:2206.14651.

ソースコード

# -*- coding: utf-8 -*-
"""
RF-DETRによる物体検出プログラム・BoT-SORTによる追跡とTTAの機能付き(COCO 80クラス)

特徴技術名: RF-DETR + BoT-SORT
出典: Robinson, I., Robicheaux, P., & Popov, M. (2025). RF-DETR: SOTA Real-Time
     Object Detection Model. Roboflow. https://github.com/roboflow/rf-detr
     Aharon, N. et al. (2022). BoT-SORT: Robust Associations Multi-Pedestrian
     Tracking. arXiv:2206.14651.

特徴機能: NMSフリーのTransformer物体検出(RF-DETR、DINOv2バックボーン)に、
         カメラモーション補正と外観特徴(ReID)を統合したBoT-SORTによる追跡を組み合わせる。
         CLAHEによる暗部補正とTTA(水平反転)による検出精度向上を実装。

学習済みモデル: RF-DETR (RFDETRSmall / RFDETRMedium / RFDETRLarge)
               - 概要: COCOデータセット80クラス対応、DINOv2バックボーン、Apache 2.0ライセンス
               - URL: 自動ダウンロード (https://github.com/roboflow/rf-detr)
               ReIDモデル: osnet_x0_25_msmt17(boxmotが自動ダウンロード)

方式設計:
  関連利用技術:
    - RF-DETR: DINOv2バックボーンを用いたTransformerベースのリアルタイム物体検出モデル
    - BoT-SORT: カルマンフィルタ・カメラモーション補正・ReIDを統合した追跡(boxmotライブラリ)
    - CLAHE: 暗部・逆光環境向けコントラスト補正
    - OpenCV: 画像・動画処理、日本語フォント表示対応

  入力と出力:
    入力: 動画(ユーザは「0:動画ファイル,1:カメラ,2:サンプル動画」のメニューで選択.
          0:動画ファイルの場合はtkinterでファイル選択.1の場合はOpenCVでカメラが開く.
          2の場合はhttps://raw.githubusercontent.com/opencv/opencv/master/samples/data/vtest.aviを使用)
    出力: OpenCV画面でリアルタイム表示。色分けされたバウンディングボックスと追跡情報を表示。
          各フレームごとにprint()で処理結果表示。プログラム終了時にresult.txtファイルに保存。

  処理手順:
    1. CLAHEによる暗部補正前処理
    2. RF-DETRによる物体検出(TTA:元画像と水平反転画像を統合)
    3. 高信頼度・低信頼度検出の色分け
    4. BoT-SORTによる追跡(動き予測・カメラモーション補正・ReID)
    5. 日本語フォント使用による結果可視化

  前処理: CLAHEによるコントラスト補正、TTA用の水平反転画像生成
  後処理: TTA結果のNMSによる統合、検出結果のフィルタリング、追跡ID管理
  追加処理: 日本語テキスト描画による視認性向上、色分け凡例表示

  調整を必要とする設定値:
    - CONF_THRESHOLD: 検出信頼度閾値(高信頼度・低信頼度検出の分離基準)
    - LOW_CONF_THRESH: 低信頼度検出の最小閾値(追跡に使用する下限)
    - NMS_THRESHOLD: TTA統合時のNMS閾値

将来方策: 検出信頼度閾値の自動調整機能、複数カメラ対応、リアルタイム性能最適化

その他の重要事項: Windows環境設計、メイリョフォント使用

前準備:
  pip install --no-user -U rfdetr opencv-python numpy pillow boxmot supervision torch
"""

# 調整可能な設定値
CONF_THRESHOLD = 0.25       # 高信頼度検出の閾値
LOW_CONF_THRESH = 0.1       # 低信頼度検出の最小閾値(追跡に使用)
NMS_THRESHOLD = 0.6         # TTA統合用NMS閾値
BOTSORT_VIZ = True          # 可視化機能の有効化
FONT_SIZE = 20
FONT_PATH = 'C:/Windows/Fonts/meiryo.ttc'

import cv2
import tkinter as tk
from tkinter import filedialog
import numpy as np
import torch
from torchvision.ops import nms
from PIL import Image, ImageDraw, ImageFont
import urllib.request
import time
from datetime import datetime
from boxmot import BotSort
from pathlib import Path

# GPU/CPU自動選択
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f'デバイス: {str(device)}')
if device.type == 'cuda':
    torch.backends.cudnn.benchmark = True

def apply_clahe(frame):
    """CLAHEによる暗部・逆光補正(YUV色空間のYチャンネルに適用)"""
    yuv = cv2.cvtColor(frame, cv2.COLOR_BGR2YUV)
    clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
    yuv[:, :, 0] = clahe.apply(yuv[:, :, 0])
    return cv2.cvtColor(yuv, cv2.COLOR_YUV2BGR)

def detect_with_tta(frame_rgb):
    """TTA(元画像+水平反転)による物体検出とNMS統合"""
    flipped_rgb = cv2.flip(frame_rgb, 1)
    w = frame_rgb.shape[1]

    all_boxes, all_scores, all_labels = [], [], []

    for img_array, is_flipped in [(frame_rgb, False), (flipped_rgb, True)]:
        detections = model.predict(img_array, threshold=LOW_CONF_THRESH)

        boxes = torch.from_numpy(detections.xyxy.astype(np.float32))
        if is_flipped and boxes.shape[0] > 0:
            boxes = boxes.clone()
            x1 = w - torch.from_numpy(detections.xyxy[:, 2].astype(np.float32))
            x2 = w - torch.from_numpy(detections.xyxy[:, 0].astype(np.float32))
            boxes[:, 0] = x1
            boxes[:, 2] = x2

        all_boxes.append(boxes)
        all_scores.append(torch.from_numpy(detections.confidence.astype(np.float32)))
        all_labels.append(torch.from_numpy(detections.class_id.astype(np.float32)))

    boxes_cat = torch.cat(all_boxes, dim=0)
    scores_cat = torch.cat(all_scores, dim=0)
    labels_cat = torch.cat(all_labels, dim=0)

    if boxes_cat.shape[0] == 0:
        return np.empty((0, 4)), np.empty((0,)), np.empty((0,))

    keep = nms(boxes_cat, scores_cat, NMS_THRESHOLD)
    return boxes_cat[keep].numpy(), scores_cat[keep].numpy(), labels_cat[keep].numpy()

frame_count = 0
results_log = []

def video_frame_processing(frame):
    global frame_count
    current_time = time.time()
    frame_count += 1

    # CLAHE前処理
    enhanced_frame = apply_clahe(frame)
    frame_rgb = cv2.cvtColor(enhanced_frame, cv2.COLOR_BGR2RGB)

    # RF-DETRによる検出(TTA付き)
    boxes, scores, labels = detect_with_tta(frame_rgb)

    high_count, low_count, new_count = 0, 0, 0

    # BoT-SORT用の検出配列(N, 6: x1,y1,x2,y2,conf,cls)を構築
    if boxes.shape[0] > 0:
        dets = np.concatenate([boxes, scores.reshape(-1, 1), labels.reshape(-1, 1)], axis=1).astype(np.float32)
    else:
        dets = np.empty((0, 6), dtype=np.float32)

    # BoT-SORTによる追跡更新
    tracks = tracker.update(dets, frame)  # --> (x1,y1,x2,y2,id,conf,cls,ind)

    ann_frame = frame.copy()
    img_pil = Image.fromarray(cv2.cvtColor(ann_frame, cv2.COLOR_BGR2RGB))
    draw = ImageDraw.Draw(img_pil)

    tracking_data = []

    if tracks.shape[0] > 0:
        for t in tracks:
            x1, y1, x2, y2, track_id, conf, cls, _ = t
            x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
            track_id = int(track_id)
            cls = int(cls)

            if conf >= CONF_THRESHOLD:
                level = '高'
                box_color = (0, 255, 0)
                high_count += 1
            else:
                level = '低'
                box_color = (255, 255, 0)
                low_count += 1

            is_new = track_id not in seen_track_ids
            if is_new:
                seen_track_ids.add(track_id)
                box_color = (0, 255, 255)
                level = '新'
                new_count += 1

            cv2.rectangle(ann_frame, (x1, y1), (x2, y2), box_color, 4)

            label_text = f"ID:{track_id} [{level}] {conf:.2f}"
            draw.text((x1, max(0, y1 - 25)), label_text, font=font, fill=box_color)

            tracking_data.append({
                'id': track_id, 'class': cls, 'confidence': conf,
                'level': level, 'bbox': (x1, y1, x2, y2)
            })

        ann_frame = cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)

    # 色分け凡例表示
    if BOTSORT_VIZ:
        img_pil_legend = Image.fromarray(cv2.cvtColor(ann_frame, cv2.COLOR_BGR2RGB))
        draw_legend = ImageDraw.Draw(img_pil_legend)
        draw_legend.text((10, 10), '緑:高信頼度 黄:低信頼度 シアン:新規トラック', font=font, fill=(255, 255, 255))
        status_text = f'高:{high_count} 低:{low_count} 新:{new_count}'
        draw_legend.text((10, 35), status_text, font=font, fill=(0, 255, 255))
        ann_frame = cv2.cvtColor(np.array(img_pil_legend), cv2.COLOR_RGB2BGR)

    result = f"フレーム:{frame_count}|高:{high_count}|低:{low_count}|新:{new_count}|" + \
             "|".join(f"ID{d['id']}:cls{d['class']}:{d['confidence']:.2f}" for d in tracking_data)

    return ann_frame, result, current_time

# メイン処理
print('========================================')
print('RF-DETR + BoT-SORT 物体検出・追跡プログラム')
print('========================================')
print('概要: RF-DETRによる物体検出とBoT-SORTによる追跡を統合')
print('特徴: CLAHE前処理とTTAにより暗所でも高精度な検出が可能')
print('注意事項:')
print('  - GPU使用時は処理が高速化されます')
print('  - 初回実行時はモデルのダウンロードに時間がかかります')
print('操作方法:')
print('  q キー: プログラム終了')
print('========================================')

print('\nモデルサイズ選択:')
print('0: RFDETRSmall(軽量・高速)')
print('1: RFDETRMedium(標準・推奨)')
print('2: RFDETRLarge(高精度)')
model_choice = input('選択: ')

print(f'\nRF-DETRモデル初期化中...')
if model_choice == '0':
    from rfdetr import RFDETRSmall
    model = RFDETRSmall()
    MODEL_NAME = 'RFDETRSmall'
elif model_choice == '2':
    from rfdetr import RFDETRLarge
    model = RFDETRLarge()
    MODEL_NAME = 'RFDETRLarge'
else:
    from rfdetr import RFDETRMedium
    model = RFDETRMedium()
    MODEL_NAME = 'RFDETRMedium'
print('RF-DETR初期化完了')

print('BoT-SORTトラッカー初期化中...')
tracker = BotSort(
    reid_weights=Path('osnet_x0_25_msmt17.pt'),
    device=device,
    half=False,
    with_reid=True,
)
seen_track_ids = set()
print('BoT-SORT初期化完了')

font = ImageFont.truetype(FONT_PATH, FONT_SIZE)
print('日本語フォント: 設定完了')

print("\n0: 動画ファイル")
print("1: カメラ")
print("2: サンプル動画")

choice = input("選択: ")

if choice == '0':
    root = tk.Tk()
    root.withdraw()
    path = filedialog.askopenfilename()
    if not path:
        exit()
    cap = cv2.VideoCapture(path)
elif choice == '1':
    cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
    if not cap.isOpened():
        cap = cv2.VideoCapture(0)
    cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
else:
    SAMPLE_URL = 'https://raw.githubusercontent.com/opencv/opencv/master/samples/data/vtest.avi'
    SAMPLE_FILE = 'vtest.avi'
    urllib.request.urlretrieve(SAMPLE_URL, SAMPLE_FILE)
    cap = cv2.VideoCapture(SAMPLE_FILE)

if not cap.isOpened():
    print('動画ファイル・カメラを開けませんでした')
    exit()

# メイン処理
print('\n=== 動画処理開始 ===')
print('操作方法:')
print('  q キー: プログラム終了')
try:
    while True:
        ret, frame = cap.read()
        if not ret:
            break

        MAIN_FUNC_DESC = "RF-DETR + BoT-SORT トラッキング"
        processed_frame, result, current_time = video_frame_processing(frame)
        cv2.imshow(MAIN_FUNC_DESC, processed_frame)
        if choice == '1':  # カメラの場合
            print(datetime.fromtimestamp(current_time).strftime("%Y-%m-%d %H:%M:%S.%f")[:-3], result)
        else:  # 動画ファイルの場合
            print(frame_count, result)
        results_log.append(result)

        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
finally:
    print('\n=== プログラム終了 ===')
    cap.release()
    cv2.destroyAllWindows()
    if results_log:
        with open('result.txt', 'w', encoding='utf-8') as f:
            f.write('=== 結果 ===\n')
            f.write(f'処理フレーム数: {frame_count}\n')
            f.write(f'使用デバイス: {str(device).upper()}\n')
            if device.type == 'cuda':
                f.write(f'GPU: {torch.cuda.get_device_name(0)}\n')
            f.write(f'使用モデル: {MODEL_NAME}\n')
            f.write('\n=== フレームごとの結果 ===\n')
            f.write('\n'.join(results_log))
        print(f'\n処理結果をresult.txtに保存しました')