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]。

主要技術とその概要

参考文献


# 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}に保存')