YOLO11によるOBB(回転物体検出)
【概要】YOLO11-OBBを使用してリアルタイム回転物体検出を実行。Enhanced Feature Extractionにより任意の角度で回転した物体を検出し、5種類のモデルサイズによる精度と速度の比較実験が可能。Windows環境での実行手順、プログラムコード、実験アイデアを含む。

目次
概要
技術名: YOLO11-OBB(You Only Look Once version 11 - Oriented Bounding Box)
発表: 2024年9月30日、Ultralytics YOLO Vision 2024 (YV24) にて正式発表
新規性・特徴: YOLO11-OBBは、Enhanced Feature Extraction(強化された特徴抽出)を採用した最新の回転物体検出技術である。C3k2(Cross Stage Partial with kernel size 2:カーネルサイズ2のクロスステージ部分接続)ブロックによる効率的な特徴抽出、C2PSA(Convolutional block with Parallel Spatial Attention:並列空間アテンション付き畳み込みブロック)による空間アテンション機構、SPPF(Spatial Pyramid Pooling - Fast:高速空間ピラミッドプーリング)による多スケール特徴統合により、任意の角度で回転した物体の検出を実現する。
技術革新:
- C3k2ブロック:2つの小さな畳み込みによる効率的なCSP Bottleneck(クロスステージ部分ボトルネック:異なる段階の特徴を部分的に接続する構造)実装、処理速度向上
- C2PSA:空間アテンション機構(画像内の重要な領域に注意を向ける仕組み)により重要な領域に焦点、回転物体の検出精度向上
- SPPF:異なるスケールでの特徴プーリング(複数の解像度で特徴を統合する手法)による多スケール回転物体検出能力向上
- 統合アーキテクチャ:単一ネットワークでの効率的な回転物体検出処理
アプリケーション例: 航空写真解析、衛星画像処理、海事・港湾管理、都市計画、農業監視、エネルギー設備検査、交通監視、ロボティクス
OBB(Oriented Object Detection)とは
OBB(Oriented Bounding Box)は、従来の軸平行バウンディングボックスとは異なり、物体の向きに合わせて回転可能な長方形で物体を囲む技術である。通常の物体検出では水平・垂直な長方形しか使用できないが、OBBでは任意の角度で回転した長方形を使用することで、斜めや回転した物体をより正確に検出・位置特定できる。
従来手法との主な違い:
- 通常の物体検出: 軸に平行な長方形(axis-aligned rectangle)のみ
- OBB: 物体の向きに合わせて回転可能な長方形、より正確な位置特定
- 適用分野: 航空写真、衛星画像、回転物体が多い産業現場で特に有効
使用する学習済みモデル
YOLO11-OBB事前学習済みモデル:
- 学習データセット: DOTAv1(15クラス回転物体検出)
- 検出可能物体: 飛行機、船舶、貯蔵タンク、野球場、テニスコート、バスケットボールコート、陸上競技場、港湾、橋梁、車両など15種類
- 出力形式: 回転バウンディングボックス + クラス確率 + 信頼度 + 回転角度
- 入力解像度: 640×640ピクセル(デフォルト)
- モデルサイズ: Nano版(軽量)からExtra Large版(高精度)まで5種類
- 技術特徴: C3k2ブロック、C2PSA空間アテンション、SPPF多スケール処理
DOTAv1データセットの15クラス
- 飛行機 (plane)
- 船舶 (ship)
- 貯蔵タンク (storage-tank)
- 野球場 (baseball-diamond)
- テニスコート (tennis-court)
- バスケットボールコート (basketball-court)
- 陸上競技場 (ground-track-field)
- 港湾 (harbor)
- 橋梁 (bridge)
- 大型車両 (large-vehicle)
- 小型車両 (small-vehicle)
- ヘリコプター (helicopter)
- ラウンドアバウト (roundabout)
- サッカー場 (soccer-ball-field)
- 水泳プール (swimming-pool)
事前準備
Python, Windsurfをインストールしていない場合の手順(インストール済みの場合は実行不要)。
- 管理者権限でコマンドプロンプトを起動する(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)。
- 以下のコマンドをそれぞれ実行する(winget コマンドは1つずつ実行)。
REM Python をシステム領域にインストール
winget install --scope machine --id Python.Python.3.12 -e --silent
REM Windsurf をシステム領域にインストール
winget install --scope machine --id Codeium.Windsurf -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
REM Windsurf のパス設定
set "WINDSURF_PATH=C:\Program Files\Windsurf"
if exist "%WINDSURF_PATH%" (
echo "%PATH%" | find /i "%WINDSURF_PATH%" >nul
if errorlevel 1 setx PATH "%PATH%;%WINDSURF_PATH%" /M >nul
)
必要なパッケージのインストール
管理者権限でコマンドプロンプトを起動し、以下のコマンドを実行する:
pip install -U torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126
pip install ultralytics opencv-python numpy pillow
YOLO11-OBB回転物体検出プログラム
概要
動画像やカメラ映像から物体を検出・認識し、その位置や種類を理解する。具体的には、入力された画像データからYOLO11-OBB(Oriented Bounding Box)アルゴリズムにより、航空画像における船舶、建物、車両等の任意角度物体(15種類の物体クラス)を検出し、回転境界ボックスによる位置推定を行う。
主要技術
- YOLO11アルゴリズム
Ultralyticsが開発した物体検出アルゴリズムである。C3k2ブロック、C2PSA空間アテンション機構、SPPFアーキテクチャを統合した特徴抽出を行う [1]。
- Oriented Bounding Box (OBB)
回転境界ボックスによる物体検出手法である。DOTAv1データセット上で開発され、航空画像における任意角度物体の高精度検出を可能にする。従来の軸平行境界ボックスと異なり、物体の回転角度に対応した境界ボックスを生成することで、密集した物体や回転した物体の検出精度を向上させる [2]。
参考文献
[1] Jocher, G., & Qiu, J. (2024). Ultralytics YOLO11 (Version 11.0.0) [Computer software]. https://github.com/ultralytics/ultralytics
[2] Xia, G. S., Bai, X., Ding, J., Zhu, Z., Belongie, S., Luo, J., Datcu, M., Pelillo, M., & Zhang, L. (2018). DOTA: A large-scale dataset for object detection in aerial images. IEEE Conference on Computer Vision and Pattern Recognition (CVPR), 3974-3983.
# プログラム名: YOLO11-OBB物体検出プログラム
# 特徴技術名: YOLO11-OBB
# 出典:
# - 論文: Khanam, R., & Hussain, M. (2024). YOLOv11: An Overview of the Key Architectural Enhancements. arXiv preprint arXiv:2410.17725.
# - DOTA論文: Xia, G. S., et al. (2018). DOTA: A Large-scale Dataset for Object Detection in Aerial Images. CVPR 2018.
# - GitHub: https://github.com/ultralytics/ultralytics
# - 公式ドキュメント: https://docs.ultralytics.com/tasks/obb/
# 特徴機能: Oriented Bounding Box (OBB) による任意角度物体検出機能。従来の軸平行境界ボックス(Axis-Aligned Bounding Box)と異なり、物体の向きに合わせて回転可能な境界ボックスによる高精度検出。航空・衛星画像において物体が様々な角度で現れる場合に特に有効で、背景領域の誤検出を大幅に削減
# 学習済みモデル:
# - yolo11n-obb.pt(DOTAv1データセット15クラス対応、Nano版、最軽量・最高速、自動ダウンロード)
# - yolo11s-obb.pt(DOTAv1データセット15クラス対応、Small版、軽量・高速、自動ダウンロード)
# - yolo11m-obb.pt(DOTAv1データセット15クラス対応、Medium版、バランス型、自動ダウンロード)
# - yolo11l-obb.pt(DOTAv1データセット15クラス対応、Large版、高精度、自動ダウンロード)
# - yolo11x-obb.pt(DOTAv1データセット15クラス対応、Extra Large版、最高精度、自動ダウンロード)
# 方式設計:
# - 関連利用技術:
# - PyTorch: 深層学習フレームワーク、CUDA対応によるGPU加速
# - OpenCV: 画像処理、カメラ制御、描画処理、動画入出力管理
# - 入力と出力: 入力: 動画(ユーザは「0:動画ファイル,1:カメラ,2:サンプル動画」のメニューで選択.0:動画ファイルの場合はtkinterでファイル選択.1の場合はOpenCVでカメラが開く.2の場合はhttps://raw.githubusercontent.com/opencv/opencv/master/samples/data/vtest.aviを使用)、出力: OpenCV画面でリアルタイム表示(検出したオブジェクトをOBBで表示)、1秒間隔でprint()による処理結果表示、プログラム終了時にresult.txtファイルに保存
# - 処理手順: 1.フレーム取得、2.YOLO11-OBB推論実行、3.DOTAv1 15クラスの検出、4.信頼度閾値による選別、5.Oriented Bounding Box描画
# - 前処理、後処理: 前処理:YOLO11-OBB内部で自動実行(640x640リサイズ、正規化)。後処理:YOLO11-OBBのNMSフリー設計により、重複除去処理が不要。信頼度による閾値フィルタリングのみ実施
# - 追加処理: 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 pillow
import cv2
import tkinter as tk
from tkinter import filedialog
import os
import torch
import numpy as np
from ultralytics import YOLO
import warnings
import time
import urllib.request
from PIL import Image, ImageDraw, ImageFont
warnings.filterwarnings('ignore')
# 日本語フォント設定
FONT = ImageFont.truetype("C:/Windows/Fonts/msgothic.ttc", 30)
# ===== 設定・定数管理 =====
# YOLO11-OBBモデル設定(デフォルト:n、変更可能:n, s, m, l, x)
MODEL_SIZE = 'n' # 使用するモデルサイズ(n=nano, s=small, m=medium, l=large, x=extra large)
MODEL_NAME = f'yolo11{MODEL_SIZE}-obb.pt'
# モデル情報
MODEL_INFO = {
'n': {'name': 'nano', 'desc': '最軽量'},
's': {'name': 'small', 'desc': '軽量'},
'm': {'name': 'medium', 'desc': '中程度'},
'l': {'name': 'large', 'desc': '高精度'},
'x': {'name': 'extra large', 'desc': '最高精度'}
}
# DOTAv1 15クラス名
DOTA_CLASSES = [
'plane', 'ship', 'storage-tank', 'baseball-diamond', 'tennis-court', 'basketball-court',
'ground-track-field', 'harbor', 'bridge', 'large-vehicle', 'small-vehicle',
'helicopter', 'roundabout', 'soccer-ball-field', 'swimming-pool'
]
# クラスごとの色生成(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(DOTA_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)
IMG_SIZE = 640 # YOLO入力画像サイズ
# 表示設定
PRINT_INTERVAL = 1.0 # 結果出力間隔(秒)
OBJECT_TEXT_Y = 90 # 検出数表示Y座標
OBJECT_COLOR = (0, 255, 0) # 検出数表示色(BGR)
# プログラム概要表示
print('=== YOLO11-OBBオブジェクト検出プログラム ===')
print('概要: リアルタイムでオブジェクトを検出し、Oriented Bounding Boxで表示します')
print('機能: YOLO11-OBBによるオブジェクト検出(DOTAv1データセット15クラス)')
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モードで実行します')
# YOLO11-OBBモデル初期化
try:
print(f'YOLO11{MODEL_SIZE}-OBBモデルを初期化中...')
model = YOLO(MODEL_NAME)
# デバイスにモデルを移動
model.to(device)
print(f'YOLO11{MODEL_SIZE}-OBBモデルの初期化が完了しました')
print(f'モデルサイズ: {MODEL_SIZE} ({MODEL_INFO[MODEL_SIZE]["name"]}={MODEL_INFO[MODEL_SIZE]["desc"]})')
except Exception as e:
print(f'YOLO11{MODEL_SIZE}-OBBモデルの初期化に失敗しました')
print(f'エラー: {e}')
exit()
print(f'{device.upper()}使用モード')
print('初期化完了')
print()
# グローバル変数
frame_count = 0
last_print_time = time.time()
results_log = []
def add_text(frame, text, y, color):
"""フレームに日本語テキストを追加する関数"""
img_pil = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(img_pil)
draw.text((10, y), text, font=FONT, fill=(color[2], color[1], color[0]))
return cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)
def video_processing(frame):
"""フレーム処理メイン関数"""
global frame_count, last_print_time, results_log
frame_count += 1
# オブジェクト検出実行(デバイス指定)
preds = model(frame, conf=CONF_THRESH, device=device,
imgsz=IMG_SIZE, verbose=False)
frame_out = preds[0].plot()
# 検出結果処理
objects = []
for pred in preds:
if hasattr(pred, 'obb') and pred.obb is not None and len(pred.obb.cls) > 0:
obj_cnt = len(pred.obb.cls)
frame_out = add_text(frame_out, f'検出数: {obj_cnt}', OBJECT_TEXT_Y, OBJECT_COLOR)
xyxyxyxy = pred.obb.xyxyxyxy.cpu().numpy()
confs = pred.obb.conf.cpu().numpy()
classes = pred.obb.cls.cpu().numpy()
# 信頼度でソート(降順)
sorted_indices = np.argsort(confs)[::-1]
xyxyxyxy = xyxyxyxy[sorted_indices]
confs = confs[sorted_indices]
classes = classes[sorted_indices]
# 各オブジェクトの処理
for i, (obb, conf, cls) in enumerate(zip(xyxyxyxy, confs, classes)):
if conf > CONF_THRESH:
class_id = int(cls)
object_data = {
'obb': obb,
'detection_conf': conf,
'class_id': class_id,
'class_name': DOTA_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
# システム情報表示(日本語対応)
info1 = f'YOLO11-OBB ({device.upper()}) | フレーム: {frame_count} | オブジェクト: {len(objects)}'
info2 = 'キー操作: q=終了'
frame_out = add_text(frame_out, info1, 10, (255, 255, 255))
frame_out = add_text(frame_out, info2, 50, (255, 255, 0))
return frame_out
# 入力選択
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('YOLO11-OBB 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('=== YOLO11-OBBオブジェクト検出結果 ===\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=== プログラム終了 ===')
使用方法
- プログラムを実行.リアルタイムで回転物体検出が開始される
- カメラを選んだ場合,カメラに向かって様々な物体(特に回転・傾斜した物体)を映すと、回転バウンディングボックスとクラス名、信頼度が表示される
- 検出された各回転物体にはクラス名と信頼度が表示される
- 'q'キーを押すとプログラムが終了する
実験・探求のアイデア
YOLO11-OBBモデル選択実験
プログラム冒頭のMODEL_NAMEを変更することで、異なるYOLO11-OBBモデルを比較できる:
yolo11n-obb.pt
:Nano版(最高効率、エッジデバイス最適)yolo11s-obb.pt
:Small版(バランス型、実用性重視)yolo11m-obb.pt
:Medium版(汎用用途、精度向上)yolo11l-obb.pt
:Large版(高精度重視、計算コスト増)yolo11x-obb.pt
:Extra Large版(最高性能、最大計算要求)
回転物体検出精度の検証実験
C2PSAの並列空間アテンション効果を定量的に評価:
- 回転角度による検出精度:物体を様々な角度に回転させた場合の検出精度測定
- 複数回転物体検出:密集環境での回転物体分離能力の評価
- 部分遮蔽対応:遮蔽された回転物体の検出復元性能
- 複雑背景処理:背景が複雑な環境での回転物体検出精度
体験・実験・探求のアイデア
アーキテクチャ改良効果の測定: C3k2とC2PSAの技術革新により、従来困難であった回転物体シーンでの性能向上を定量的に測定
リアルタイム応用実験:
- 航空写真解析:飛行機、滑走路、建物の回転検出
- 海事監視:様々な角度の船舶自動検出
- 農業監視:ドローンからの農機具・作物列の回転検出
- 都市計画:衛星画像からの道路・建物の向き検出
多スケール回転検出能力の実験: SPPFによる異なるサイズの回転物体同時検出性能を評価
回転角度精度実験: 様々な回転角度での検出精度とバウンディングボックスの正確性を測定