YOLOv12による物体検出(ソースコードと実行結果)

Python開発環境,ライブラリ類
ここでは、最低限の事前準備について説明する。機械学習や深層学習を行う場合は、NVIDIA CUDA、Visual Studio、Cursorなどを追加でインストールすると便利である。これらについては別ページ https://www.kkaneko.jp/cc/dev/aiassist.htmlで詳しく解説しているので、必要に応じて参照してください。
Python 3.12 のインストール
インストール済みの場合は実行不要。
管理者権限でコマンドプロンプトを起動(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行する。管理者権限は、wingetの--scope machineオプションでシステム全体にソフトウェアをインストールするために必要である。
REM Python をシステム領域にインストール
winget install --scope machine --id Python.Python.3.12 -e --silent
REM Python のパス設定
set "PYTHON_PATH=C:\Program Files\Python312"
set "PYTHON_SCRIPTS_PATH=C:\Program Files\Python312\Scripts"
echo "%PATH%" | find /i "%PYTHON_PATH%" >nul
if errorlevel 1 setx PATH "%PATH%;%PYTHON_PATH%" /M >nul
echo "%PATH%" | find /i "%PYTHON_SCRIPTS_PATH%" >nul
if errorlevel 1 setx PATH "%PATH%;%PYTHON_SCRIPTS_PATH%" /M >nul
【関連する外部ページ】
Python の公式ページ: https://www.python.org/
AI エディタ Windsurf のインストール
Pythonプログラムの編集・実行には、AI エディタの利用を推奨する。ここでは,Windsurfのインストールを説明する。
管理者権限でコマンドプロンプトを起動(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行して、Windsurfをシステム全体にインストールする。管理者権限は、wingetの--scope machineオプションでシステム全体にソフトウェアをインストールするために必要となる。
winget install --scope machine Codeium.Windsurf -e --silent
【関連する外部ページ】
Windsurf の公式ページ: https://windsurf.com/
Gitのインストール
管理者権限でコマンドプロンプトを起動(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行する。管理者権限は、wingetの--scope machineオプションでシステム全体にソフトウェアをインストールするために必要となる。
REM Git をシステム領域にインストール
winget install --scope machine --id Git.Git -e --silent
REM Git のパス設定
set "GIT_PATH=C:\Program Files\Git\cmd"
if exist "%GIT_PATH%" (
echo "%PATH%" | find /i "%GIT_PATH%" >nul
if errorlevel 1 setx PATH "%PATH%;%GIT_PATH%" /M >nul
)
必要なライブラリのインストール
コマンドプロンプトを管理者として実行(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行する
pip install -U torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126
pip install ultralytics opencv-python numpy gitpython
YOLOv12による物体検出プログラム
概要
このプログラムは、YOLOv12モデルを用いて、入力された動画から80種類の物体(人、車、動物、日用品など)をリアルタイムで検出し、各物体の位置と種類を特定する。
主要技術とその概要
- YOLOv12(You Only Look Once version 12):物体検出のための深層学習モデルである。Area Attention機構とR-ELAN(Residual Efficient Layer Aggregation Networks)を採用し、CNNベースモデルと同等の処理速度でAttention機構の性能優位性を実現している[1]。
参考文献
[1] "YOLOv12," GitHub repository, 2025. [Online]. Available: https://github.com/sunsmarterjie/yolov12
ソースコード
# プログラム名: YOLOv12物体検出プログラム
# 特徴技術名: YOLOv12 (You Only Look Once version 12)
# 出典: Tian, Yunjie and Ye, Qixiang and Doermann, David (2025). YOLOv12: Attention-Centric Real-Time Object Detectors. arXiv preprint arXiv:2502.12524. GitHub: https://github.com/sunsmarterjie/yolov12
# 特徴機能: Area Attention機構(受容野を大きく保ちながら従来のattention機構と比較して計算コストを削減し、CNNベースモデルと同等の速度でAttention機構の性能優位性を実現)による物体検出
# 学習済みモデル: yolov12n.pt (COCOデータセットで事前学習済み、80クラス対応、nanoバリアント、自動ダウンロード対応)で、クラス0が人物(person)を検出可能
# モデルサイズ選択可能(デフォルト:n):
# n (nano): yolov12n.pt - 最軽量
# s (small): yolov12s.pt - 軽量
# m (medium): yolov12m.pt - バランス型
# b (balanced): yolov12b.pt - バランス強化型
# l (large): yolov12l.pt - 高精度
# x (extra large): yolov12x.pt - 最高精度
# 方式設計:
# - 関連利用技術:
# - PyTorch: 深層学習フレームワーク、CUDA対応によるGPU加速
# - OpenCV: 画像処理、カメラ制御、描画処理、動画入出力管理
# - 入力と出力: 入力: 動画(ユーザは「0:動画ファイル,1:カメラ,2:サンプル動画」のメニューで選択.0:動画ファイルの場合はtkinterでファイル選択.1の場合はOpenCVでカメラが開く.2の場合はhttps://raw.githubusercontent.com/opencv/opencv/master/samples/data/vtest.aviを使用)、出力: OpenCV画面でリアルタイム表示(検出したオブジェクトをバウンディングボックスで表示)、1秒間隔でprint()による処理結果表示、プログラム終了時にresult.txtファイルに保存
# - 処理手順: 1.フレーム取得、2.YOLOv12推論実行(Area Attention機構)、3.COCO 80クラス全体の検出、4.信頼度閾値による選別、5.バウンディングボックス描画
# - 前処理、後処理: 前処理:YOLOv12内部で自動実行(640x640リサイズ、正規化)。後処理:YOLOv12のArea Attention機構により、効率的な物体検出処理。信頼度による閾値フィルタリングのみ実施
# - 追加処理: CUDA/CPU自動検出機能により、GPU搭載環境では自動的に高速化。検出結果の信頼度降順ソートにより重要な検出を優先表示
# - 調整を必要とする設定値: CONF_THRESH(オブジェクト検出信頼度閾値、デフォルト0.5)- 値を上げると誤検出が減少するが検出漏れが増加
# 将来方策: CONF_THRESHの動的調整機能。フレーム毎の検出数を監視し、検出数が閾値を超えた場合は信頼度を上げ、検出数が少ない場合は下げる適応的制御の実装
# その他の重要事項: Windows環境専用設計、CUDA対応GPU推奨(自動検出・CPUフォールバック機能付き)、初回実行時は学習済みモデルの自動ダウンロード
# 前準備:
# - pip install -U torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126
# - pip install ultralytics opencv-python numpy gitpython
import cv2
import tkinter as tk
from tkinter import filedialog
import os
import torch
import numpy as np
import warnings
import time
import urllib.request
import sys
import git
warnings.filterwarnings('ignore')
# ===== YOLOv12セットアップ =====
REPO_URL = 'https://github.com/sunsmarterjie/yolov12.git'
REPO_DIR = 'yolov12'
# YOLOv12の自動セットアップ
if not os.path.exists(REPO_DIR):
print('YOLOv12のセットアップを開始します...')
print('YOLOv12リポジトリをクローン中...')
try:
git.Repo.clone_from(REPO_URL, REPO_DIR)
except Exception as e:
print(f'YOLOv12リポジトリのクローンに失敗しました: {e}')
exit()
# YOLOv12ディレクトリをパスに追加
sys.path.insert(0, os.path.abspath(REPO_DIR))
yolov12_ultralytics = os.path.join(REPO_DIR, 'ultralytics')
if os.path.exists(yolov12_ultralytics):
sys.path.insert(0, os.path.abspath(yolov12_ultralytics))
# YOLOv12固有のultralyticsをインポート
try:
from ultralytics import YOLO
except ImportError:
sys.path.insert(0, os.path.abspath(REPO_DIR))
try:
from ultralytics import YOLO
except ImportError as e:
print(f'YOLOv12のインポートに失敗しました: {e}')
exit()
# ===== 設定・定数管理 =====
# YOLOv12モデル設定(デフォルト:n、変更可能:n, s, m, b, l, x)
MODEL_SIZE = 'n' # 使用するモデルサイズ(n=nano, s=small, m=medium, b=balanced, l=large, x=extra large)
MODEL_NAME = f'yolov12{MODEL_SIZE}.pt'
# モデル情報
MODEL_INFO = {
'n': {'name': 'nano', 'desc': '最軽量'},
's': {'name': 'small', 'desc': '軽量'},
'm': {'name': 'medium', 'desc': '中程度'},
'b': {'name': 'balanced', 'desc': 'バランス強化'},
'l': {'name': 'large', 'desc': '高精度'},
'x': {'name': 'extra large', 'desc': '最高精度'}
}
# COCO 80クラス名
COCO_CLASSES = [
'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light',
'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow',
'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee',
'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard',
'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple',
'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch',
'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone',
'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear',
'hair drier', 'toothbrush'
]
# クラスごとの色生成(HSVからBGRに変換)
def generate_colors(num_classes):
colors = []
for i in range(num_classes):
hue = int(180.0 * i / num_classes)
hsv = np.uint8([[[hue, 255, 255]]])
bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)[0][0]
colors.append((int(bgr[0]), int(bgr[1]), int(bgr[2])))
return colors
CLASS_COLORS = generate_colors(len(COCO_CLASSES))
SAMPLE_URL = 'https://raw.githubusercontent.com/opencv/opencv/master/samples/data/vtest.avi'
SAMPLE_FILE = 'vtest.avi'
RESULT_FILE = 'result.txt'
# カメラ設定
WINDOW_WIDTH = 1280 # カメラ解像度幅
WINDOW_HEIGHT = 720 # カメラ解像度高さ
FPS = 30 # フレームレート
# 検出パラメータ(調整可能)
CONF_THRESH = 0.5 # オブジェクト検出信頼度閾値(0.0-1.0)
# 表示設定
PRINT_INTERVAL = 1.0 # 結果出力間隔(秒)
# プログラム概要表示
print('=== YOLOv12オブジェクト検出プログラム ===')
print('概要: リアルタイムでオブジェクトを検出し、バウンディングボックスで表示します')
print('機能: YOLOv12によるオブジェクト検出(COCOデータセット80クラス)')
print('操作: qキーで終了')
print('出力: 1秒間隔での処理結果表示、終了時にresult.txt保存')
print()
# システム初期化
print('システム初期化中...')
start_time = time.time()
# GPU/CPU自動選択
if torch.cuda.is_available():
device = 'cuda'
print(f'GPU検出: {torch.cuda.get_device_name(0)}')
print(f'CUDA バージョン: {torch.version.cuda}')
else:
device = 'cpu'
print('GPUが利用できません。CPUモードで実行します')
# YOLOv12モデル初期化
try:
print(f'YOLOv12{MODEL_SIZE}モデルを初期化中...')
model = YOLO(MODEL_NAME)
# デバイスにモデルを移動
model.to(device)
print(f'YOLOv12{MODEL_SIZE}モデルの初期化が完了しました')
print(f'モデルサイズ: {MODEL_SIZE} ({MODEL_INFO[MODEL_SIZE]["name"]}={MODEL_INFO[MODEL_SIZE]["desc"]})')
except Exception as e:
print(f'YOLOv12{MODEL_SIZE}モデルの初期化に失敗しました')
print(f'エラー: {e}')
exit()
print(f'{device.upper()}使用モード')
print('初期化完了')
print()
# グローバル変数
frame_count = 0
last_print_time = time.time()
results_log = []
def video_processing(frame):
"""フレーム処理メイン関数"""
global frame_count, last_print_time, results_log
frame_count += 1
# オブジェクト検出実行(デバイス指定)
results = model(frame, device=device, verbose=False)
objects = []
if results[0].boxes is not None:
boxes = results[0].boxes.xyxy.cpu().numpy()
confs = results[0].boxes.conf.cpu().numpy()
classes = results[0].boxes.cls.cpu().numpy()
# 信頼度でソート(降順)
sorted_indices = np.argsort(confs)[::-1]
boxes = boxes[sorted_indices]
confs = confs[sorted_indices]
classes = classes[sorted_indices]
# 各オブジェクトの処理
for i, (box, conf, cls) in enumerate(zip(boxes, confs, classes)):
if conf > CONF_THRESH:
x1, y1, x2, y2 = map(int, box)
class_id = int(cls)
object_data = {
'box': (x1, y1, x2, y2),
'detection_conf': conf,
'class_id': class_id,
'class_name': COCO_CLASSES[class_id]
}
objects.append(object_data)
# 1秒間隔での出力
current_time = time.time()
if objects and current_time - last_print_time >= PRINT_INTERVAL:
# クラス別検出数をカウント
class_counts = {}
for obj in objects:
class_name = obj['class_name']
class_counts[class_name] = class_counts.get(class_name, 0) + 1
output = f'フレーム {frame_count}: {len(objects)}個検出'
for class_name, count in class_counts.items():
output += f' | {class_name}: {count}個'
print(output)
results_log.append(output)
last_print_time = current_time
# 描画処理
for i, obj in enumerate(objects):
x1, y1, x2, y2 = obj['box']
class_id = obj['class_id']
class_name = obj['class_name']
color = CLASS_COLORS[class_id]
# バウンディングボックス
cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2)
# ラベル表示
label1 = f'{class_name}'
label2 = f'Conf:{obj["detection_conf"]:.1%}'
cv2.putText(frame, label1, (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, color, 2)
cv2.putText(frame, label2, (x1, y2+15), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1)
# システム情報表示
info1 = f'YOLOv12 ({device.upper()}) | Frame: {frame_count} | Objects: {len(objects)}'
info2 = 'Press: q=Quit'
cv2.putText(frame, info1, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
cv2.putText(frame, info2, (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 0), 1)
return frame
# 入力選択
print('0: 動画ファイル')
print('1: カメラ')
print('2: サンプル動画')
choice = input('選択: ')
temp_file = None
if choice == '0':
root = tk.Tk()
root.withdraw()
path = filedialog.askopenfilename()
if not path:
exit()
cap = cv2.VideoCapture(path)
if not cap.isOpened():
print(f'動画ファイルを開けませんでした: {path}')
exit()
elif choice == '1':
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, WINDOW_WIDTH)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, WINDOW_HEIGHT)
cap.set(cv2.CAP_PROP_FPS, FPS)
if not cap.isOpened():
print('カメラを開けませんでした')
exit()
elif choice == '2':
# サンプル動画ダウンロード・処理
try:
urllib.request.urlretrieve(SAMPLE_URL, SAMPLE_FILE)
temp_file = SAMPLE_FILE
cap = cv2.VideoCapture(SAMPLE_FILE)
if not cap.isOpened():
print(f'サンプル動画を開けませんでした: {SAMPLE_FILE}')
exit()
print('サンプル動画のダウンロードが完了しました')
except Exception as e:
print(f'動画のダウンロードに失敗しました: {SAMPLE_URL}')
print(f'エラー: {e}')
exit()
else:
print('無効な選択です')
exit()
# 動画処理開始メッセージ
print('\n=== 動画処理開始 ===')
print('操作方法:')
print(' q キー: プログラム終了')
print()
# メイン処理
try:
while True:
cap.grab()
ret, frame = cap.retrieve()
if not ret:
break
processed_frame = video_processing(frame)
cv2.imshow('YOLOv12 Object Detection', processed_frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
finally:
cap.release()
cv2.destroyAllWindows()
# 結果保存
if results_log:
with open(RESULT_FILE, 'w', encoding='utf-8') as f:
f.write('=== YOLOv12オブジェクト検出結果 ===\n')
f.write(f'処理フレーム数: {frame_count}\n')
f.write(f'使用デバイス: {device.upper()}\n')
if device == 'cuda':
f.write(f'GPU: {torch.cuda.get_device_name(0)}\n')
f.write('\n')
f.write('\n'.join(results_log))
print(f'\n処理結果を{RESULT_FILE}に保存しました')
if temp_file and os.path.exists(temp_file):
os.remove(temp_file)
print('\n=== プログラム終了 ===')