Mask2Former による動画セマンティックセグメンテーション(ソースコードと実行結果)

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/
必要なライブラリのインストール
コマンドプロンプトを管理者として実行(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行する
pip install -U torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126
pip install hf_xet
pip install transformers pillow opencv-python
Mask2Former による動画セマンティックセグメンテーションプログラム
概要
このプログラムは動画フレームから、画像内の各画素を意味カテゴリに分類するセマンティックセグメンテーションを行う。[1]。
主要技術とその概要
セマンティックセグメンテーション
画像内の各画素にクラスラベルを割り当てる。物体検出が矩形領域での物体認識に留まるのに対し、画素レベルでの詳細な領域分割を実現する [1]。
Transformerアーキテクチャ:
注意機構に基づく深層学習アーキテクチャである。画像の局所特徴だけでなく、大域的な関係性を学習する [2]。
参考文献
- [1] IBM. (n.d.). What Is Semantic Segmentation? IBM Think Topics. https://www.ibm.com/think/topics/semantic-segmentation
- [2] Vaswani, A., Shazeer, N., Parmar, N., Uszkoreit, J., Jones, L., Gomez, A. N., Kaiser, L., & Polosukhin, I. (2017). Attention Is All You Need. In Advances in Neural Information Processing Systems (Vol. 30). arXiv:1706.03762
# Mask2Former による動画セマンティックセグメンテーション
# 特徴技術名: Mask2Former(Masked-attention Mask Transformer for Universal Image Segmentation)
# 出典: Cheng, B., Misra, I., Schwing, A. G., Kirillov, A., & Girdhar, R. (2022). Masked-attention Mask Transformer for Universal Image Segmentation. In Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (pp. 1290-1299). arXiv:2112.01527
# 特徴機能: Masked-attention Mechanism - 予測されたマスク領域内でクロスアテンションを制約することで局所化された特徴を抽出し、セマンティック、インスタンス、パノプティックセグメンテーションを統一的に処理する革新的なアテンション機構
# 学習済みモデル: facebook/mask2former-swin-large-ade-semantic(Swin-Transformer-Large バックボーンを使用)。ADE20kデータセット(150の屋内・屋外シーン150クラス、25,574枚の訓練画像、2,000枚の検証画像)で学習済み。セマンティックセグメンテーション専用に最適化。URL: https://huggingface.co/facebook/mask2former-swin-large-ade-semantic
# 方式設計:
# 関連利用技術: HuggingFace Transformers(モデル読み込み・推論)、PyTorch(深層学習フレームワーク)、PIL(画像前処理)、OpenCV(動画入出力・可視化)、tkinter(ファイル選択UI)、urllib(リソースダウンロード)、NumPy(数値計算)
# 入力と出力: 入力: 動画(ユーザは「0:動画ファイル,1:カメラ,2:サンプル動画」のメニューで選択.0:動画ファイルの場合はtkinterでファイル選択.1の場合はOpenCVでカメラが開く.2の場合はhttps://github.com/opencv/opencv/raw/master/samples/data/vtest.aviを使用)、出力: セマンティックセグメンテーション結果をOpenCV画面でリアルタイムに表示.OpenCV画面内に処理結果をテキストで表示.1秒間隔でprint()で処理結果を表示.プログラム終了時にresult.txtファイルに保存
# 処理手順: 1. HuggingFace AutoImageProcessorとMask2FormerForUniversalSegmentationの読み込み、2. 動画フレーム取得とPIL形式への変換、3. Masked-attention Mechanismによるセマンティックセグメンテーション実行、4. post_process_semantic_segmentationによる後処理、5. カラーマップ適用と可視化、6. リアルタイム表示と結果記録
# 前処理、後処理: 前処理: BGR→RGB変換、PIL.Image形式への変換、プロセッサによる正規化・テンソル化、後処理: セマンティックマップ抽出、ランダムカラーマップ生成、オーバーレイ合成
# 追加処理: GPU/CPU自動選択による推論最適化、フレーム処理時間計測によるパフォーマンス監視、検出クラス数のリアルタイム表示
# 調整を必要とする設定値: なし(事前学習済みモデルを使用し、動画解像度に自動適応)
# 将来方策: 異なる学習済みモデル(COCO、Cityscapes等)への切り替え機能の実装による、用途に応じた最適なセグメンテーション性能の実現
# その他の重要事項: Windows環境でのCUDA利用によるGPU加速対応、リアルタイム処理のためのフレームバッファリング最適化
# 前準備: pip install -U torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126
# pip install transformers pillow opencv-python
# 定数定義
RANDOM_SEED = 42
ADE20K_CLASSES = 150
OVERLAY_ALPHA = 0.6
SEG_ALPHA = 0.4
PRINT_INTERVAL = 1.0
TEXT_COLOR = (255, 255, 255)
FONT_SCALE = 0.7
FONT_THICKNESS = 2
SAMPLE_VIDEO_URL = 'https://github.com/opencv/opencv/blob/master/samples/data/vtest.avi?raw=true'
RESULT_FILE = 'result.txt'
import cv2
import torch
import numpy as np
from PIL import Image
from transformers import AutoImageProcessor, Mask2FormerForUniversalSegmentation
import tkinter as tk
from tkinter import filedialog
import urllib.request
import os
import time
proc = None
mdl = None
dev = None
frame_cnt = 0
res_log = []
last_print = 0
cmap = None
def load_model():
global proc, mdl, dev, cmap
print('Mask2Formerモデルを読み込み中...')
np.random.seed(RANDOM_SEED)
cmap = np.random.randint(0, 255, size=(ADE20K_CLASSES, 3), dtype=np.uint8)
try:
proc = AutoImageProcessor.from_pretrained('facebook/mask2former-swin-large-ade-semantic')
mdl = Mask2FormerForUniversalSegmentation.from_pretrained('facebook/mask2former-swin-large-ade-semantic')
dev = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
mdl.to(dev)
print(f'モデルをデバイス {dev} に読み込み完了')
except Exception as e:
print(f'モデルの読み込みに失敗しました: {e}')
exit()
def process_frame(frame):
pil_img = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
inputs = proc(images=pil_img, return_tensors='pt').to(dev)
with torch.no_grad():
outputs = mdl(**inputs)
seg_map = proc.post_process_semantic_segmentation(
outputs, target_sizes=[pil_img.size[::-1]]
)[0]
return seg_map.cpu().numpy()
def apply_cmap(seg_map):
global cmap
unique_cls = np.unique(seg_map)
colored = np.zeros((*seg_map.shape, 3), dtype=np.uint8)
for cls_id in unique_cls:
mask = seg_map == cls_id
colored[mask] = cmap[cls_id % ADE20K_CLASSES]
return colored
def video_processing(frame):
global frame_cnt, res_log, last_print
start = time.time()
seg_map = process_frame(frame)
proc_time = time.time() - start
colored_seg = apply_cmap(seg_map)
overlay = cv2.addWeighted(frame, OVERLAY_ALPHA, colored_seg, SEG_ALPHA, 0)
unique_cls = np.unique(seg_map)
num_cls = len(unique_cls)
cv2.putText(overlay, f'Classes: {num_cls}', (10, 30), cv2.FONT_HERSHEY_SIMPLEX, FONT_SCALE, TEXT_COLOR, FONT_THICKNESS)
cv2.putText(overlay, f'Time: {proc_time:.2f}s', (10, 60), cv2.FONT_HERSHEY_SIMPLEX, FONT_SCALE, TEXT_COLOR, FONT_THICKNESS)
cv2.putText(overlay, f'Frame: {frame_cnt}', (10, 90), cv2.FONT_HERSHEY_SIMPLEX, FONT_SCALE, TEXT_COLOR, FONT_THICKNESS)
curr_time = time.time()
if curr_time - last_print >= PRINT_INTERVAL:
result = f'Frame {frame_cnt}: {num_cls} classes detected, Process time: {proc_time:.2f}s'
print(result)
res_log.append(result)
last_print = curr_time
frame_cnt += 1
return overlay
print('Mask2Former による動画セマンティックセグメンテーション')
print('操作方法: qキーで終了')
load_model()
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)
elif choice == '1':
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
elif choice == '2':
filename = 'vtest.avi'
try:
urllib.request.urlretrieve(SAMPLE_VIDEO_URL, filename)
temp_file = filename
cap = cv2.VideoCapture(filename)
except Exception as e:
print(f'動画のダウンロードに失敗しました: {SAMPLE_VIDEO_URL}')
print(f'エラー: {e}')
exit()
else:
print('無効な選択です')
exit()
try:
while True:
cap.grab()
ret, frame = cap.retrieve()
if not ret:
break
processed_frame = video_processing(frame)
cv2.imshow('Video', processed_frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
finally:
cap.release()
cv2.destroyAllWindows()
if temp_file:
os.remove(temp_file)
with open(RESULT_FILE, 'w', encoding='utf-8') as f:
for result in res_log:
f.write(result + '\n')
print(f'{RESULT_FILE}に保存')