Layumi's Person Re-ID Baseline with Swin Transformer による画像全体の特徴量算出(ソースコードと実行結果)


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 timm opencv-python pillow requests gdown
Layumi's Person Re-ID Baseline with Swin Transformer (人体全身特徴抽出)プログラム
概要
カメラから入力された動画フレームの全体を処理し、人物の外観特徴を認識して512次元の特徴ベクトルに変換する。直前のフレームと,特徴ベクトルを比較することも行っている。
このプログラムは人物再識別(Person Re-identification)のための画像の特徴ベクトル化を行う。人物再識別は,異なるカメラや時間において撮影された人物の画像から、同一人物を識別する[1]。このプログラムではSwin Transformerバックボーンを用いて人体全身の外観特徴を512次元のベクトルとして抽出し、フレーム間のコサイン類似度とユークリッド距離を計算市hy持する。
主要技術
-
人物再識別(Person Re-identification)
複数の重複しないカメラビュー間で同一人物を識別するコンピュータビジョンタスクである[2]。人物の外観、体型、服装などの特徴を用いて異なるフレームで同一人物を対応付ける。監視システム、小売店舗、空港などでの応用が進んでいる[3]。このプログラムではMarket-1501データセット[4]で事前学習されたモデルを使用している。Market-1501は1501人の歩行者を6台のカメラで撮影した32,668枚の画像から構成される大規模データセットである[5]。
-
Swin Transformer
階層的シフテッドウィンドウ自己注意機構を用いたビジョントランスフォーマーアーキテクチャである[6]。従来のTransformerが画像全体に対して自己注意を計算するのに対し、Swin Transformerは局所的なウィンドウ内で自己注意を計算し、ウィンドウをシフトすることでグローバルな情報交換を実現する。これにより画像サイズに対して線形の計算複雑度を達成している[7]。
参考文献
- [1] Ye, M., Shen, J., Lin, G., Xiang, T., Shao, L., & Hoi, S. C. (2021). Deep learning for person re-identification: A survey and outlook. IEEE transactions on pattern analysis and machine intelligence, 44(6), 2872-2893.
- [2] Gou, M., Karanam, S., Liu, W., Camps, O., & Radke, R. J. (2017). DukeMTMC4ReID: A large-scale multi-camera person re-identification dataset. In Proceedings of the IEEE conference on computer vision and pattern recognition workshops (pp. 10-19).
- [3] Zheng, L., Yang, Y., & Hauptmann, A. G. (2016). Person re-identification: Past, present and future. arXiv preprint arXiv:1610.02984.
- [4] Zheng, L., Shen, L., Tian, L., Wang, S., Wang, J., & Tian, Q. (2015). Scalable person re-identification: A benchmark. In Proceedings of the IEEE international conference on computer vision (pp. 1116-1124).
- [5] Sun, Y., Zheng, L., Yang, Y., Tian, Q., & Wang, S. (2018). Beyond part models: Person retrieval with refined part pooling (and a strong convolutional baseline). In Proceedings of the European conference on computer vision (ECCV) (pp. 480-496).
- [6] Liu, Z., Lin, Y., Cao, Y., Hu, H., Wei, Y., Zhang, Z., ... & Guo, B. (2021). Swin transformer: Hierarchical vision transformer using shifted windows. In Proceedings of the IEEE/CVF international conference on computer vision (pp. 10012-10022).
- [7] Liu, Z., Hu, H., Lin, Y., Yao, Z., Xie, Z., Wei, Y., ... & Guo, B. (2022). Swin transformer v2: Scaling up capacity and resolution. In Proceedings of the IEEE/CVF conference on computer vision and pattern recognition (pp. 2648-2658).
ソースコード
# Layumi's Person Re-ID Baseline with Swin Transformer (人体全身特徴抽出)
# 実装基盤: Layumi's PyTorch ReID Baseline
# - 小さく、使いやすく、強力なPyTorch実装のperson/vehicle re-ID基本モデル
# - GitHub: https://github.com/layumi/Person_reID_baseline_pytorch
# - 著者: Liang Zheng, Zhedong Zheng, Yi Yang
#
# バックボーン技術: Swin Transformer
# - 出典: Liu, Z., Lin, Y., Cao, Y., Hu, H., Wei, Y., Zhang, Z., ... & Guo, B. (2021).
# Swin transformer: Hierarchical vision transformer using shifted windows.
# In Proceedings of the IEEE/CVF international conference on computer vision (pp. 10012-10022).
# - 特徴: 階層的シフテッドウィンドウ自己注意機構
#
# Person Re-ID関連論文:
# - Zheng, L., Shen, L., Tian, L., Wang, S., Wang, J., & Tian, Q. (2015).
# Scalable person re-identification: A benchmark.
# In Proceedings of the IEEE international conference on computer vision (pp. 1116-1124).
# - Sun, Y., Zheng, L., Yang, Y., Tian, Q., & Wang, S. (2018).
# Beyond part models: Person retrieval with refined part pooling (and a strong convolutional baseline).
# In Proceedings of the European conference on computer vision (ECCV) (pp. 480-496).
#
# === 学習済みモデルの詳細 ===
# データセット: Market-1501 (人体全身画像データセット)
# - 1501人の歩行者画像
# - 画像サイズ: 128×64ピクセル (縦長の人体全身)
# - 6台のカメラで撮影された32,668枚の人体画像
# - 顔認識用ではなく、人体全体の外観認識用
#
# 学習内容:
# - 751人分の人物ID分類 (訓練用)
# - 服装の色・パターン
# - 体型・身長の相対的特徴
# - 持ち物(カバン、リュックなど)
# - 姿勢・全体的な外観
# - 顔の詳細は解像度的に認識不可能
#
# Layumiベースライン実装の特徴:
# - Person Re-Identification (人物再識別) 専用
# - 異なるカメラ間での同一人物追跡を目的
# - Strong Baseline手法を採用
# - BNNeck構造による特徴学習
# - 学習済みモデル: https://drive.google.com/open?id=1XVEYb0TN2SbBYOqf8SzazfYZlpH9CxyE
# - 性能: Rank@1精度 92.75%, mAP 79.70%
#
# 方式設計:
# 関連利用技術: PyTorch、timm、OpenCV、PIL、gdown
# 入力と出力: 入力: 動画フレーム、出力: 512次元人体特徴ベクトル、フレーム間類似度
# 処理手順: 学習済みモデルダウンロード、ft_net_swin読み込み、フレーム処理、特徴抽出、類似度計算
# 前処理、後処理: リサイズ(224,224)、ImageNet正規化、テンソル変換
# 追加処理: ClassBlock (BNNeck)による512次元特徴ベクトル生成
# 調整を必要とする設定値: linear_num=512(特徴ベクトル次元数)、droprate=0.5
# 算出・計算処理の検証: コサイン類似度によるフレーム間の外観変化検出
#
# 重要事項:
# - 本来は人体検出後の切り出し画像に適用すべき
# - 現在は画像全体を処理(背景含む)
# - 人体が映っている場合に効果的
#
# 前準備: pip install -U torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126
# pip install timm opencv-python pillow requests gdown
import cv2
import tkinter as tk
from tkinter import filedialog
import urllib
import urllib.request
import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision.transforms as transforms
from PIL import Image, ImageDraw, ImageFont
import timm
import time
from datetime import datetime
import os
import numpy as np
import json
# gdownモジュールのインポート処理
try:
import gdown
GDOWN_AVAILABLE = True
except ImportError:
GDOWN_AVAILABLE = False
print("警告: gdownモジュールが見つかりません。pip install gdownを実行してください。")
# GPU/CPU自動選択
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f'デバイス: {str(device)}')
# 日本語フォント設定
FONT_PATH = 'C:/Windows/Fonts/meiryo.ttc'
FONT_SIZE = 20
try:
font_japanese = ImageFont.truetype(FONT_PATH, FONT_SIZE)
except:
font_japanese = None
print("日本語フォントの読み込みに失敗しました。英語表示になります。")
frame_count = 0
results_log = []
feature_vectors_log = [] # 特徴ベクトルを保存
previous_feature = None # 前フレームの特徴ベクトル保存用
previous_frame_number = None # 前フレーム番号
def weights_init_kaiming(m):
"""Kaiming初期化 (He initialization)
参考: He, K., Zhang, X., Ren, S., & Sun, J. (2015).
Delving deep into rectifiers: Surpassing human-level performance on imagenet classification."""
classname = m.__class__.__name__
if classname.find('Linear') != -1:
nn.init.kaiming_normal_(m.weight, a=0, mode='fan_out')
nn.init.constant_(m.bias, 0.0)
elif classname.find('Conv') != -1:
nn.init.kaiming_normal_(m.weight, a=0, mode='fan_in')
if m.bias is not None:
nn.init.constant_(m.bias, 0.0)
elif classname.find('BatchNorm') != -1:
if m.affine:
nn.init.constant_(m.weight, 1.0)
nn.init.constant_(m.bias, 0.0)
def weights_init_classifier(m):
"""分類層の初期化"""
classname = m.__class__.__name__
if classname.find('Linear') != -1:
nn.init.normal_(m.weight, std=0.001)
if m.bias is not None:
nn.init.constant_(m.bias, 0.0)
class ClassBlock(nn.Module):
"""BNNeck構造を含む分類ブロック
参考: Luo, H., Gu, Y., Liao, X., Lai, S., & Jiang, W. (2019).
Bag of tricks and a strong baseline for deep person re-identification."""
def __init__(self, input_dim, class_num, droprate, relu=False, bnorm=True, linear=512, return_f=False):
super(ClassBlock, self).__init__()
self.return_f = return_f
add_block = []
if linear > 0:
add_block += [nn.Linear(input_dim, linear)]
else:
linear = input_dim
if bnorm:
add_block += [nn.BatchNorm1d(linear)] # BNNeck
if relu:
add_block += [nn.LeakyReLU(0.1)]
if droprate > 0:
add_block += [nn.Dropout(p=droprate)]
add_block = nn.Sequential(*add_block)
add_block.apply(weights_init_kaiming)
classifier = []
classifier += [nn.Linear(linear, class_num)]
classifier = nn.Sequential(*classifier)
classifier.apply(weights_init_classifier)
self.linear_num = linear
self.add_block = add_block
self.classifier = classifier
def forward(self, x):
x = self.add_block(x)
if self.return_f:
f = x
x = self.classifier(x)
return [x, f]
else:
x = self.classifier(x)
return x
class ft_net_swin(nn.Module):
"""Layumi's ReID Baseline with Swin Transformer backbone
Layumiベースラインアーキテクチャ + Swin Transformerバックボーン"""
def __init__(self, class_num=751, droprate=0.5, stride=2, circle=False, linear_num=512):
super(ft_net_swin, self).__init__()
# Swin Transformerバックボーン
model_ft = timm.create_model('swin_base_patch4_window7_224', pretrained=True, drop_path_rate=0.2)
model_ft.head = nn.Sequential() # 元の分類ヘッドを削除
self.model = model_ft
self.circle = circle
self.avgpool1d = nn.AdaptiveAvgPool1d(1)
self.avgpool2d = nn.AdaptiveAvgPool2d((1,1))
# Layumiベースラインの分類ブロック(BNNeck付き)
# Market-1501データセット751人分の人体特徴を学習
self.classifier = ClassBlock(1024, class_num, droprate, linear=linear_num, return_f=circle)
def forward(self, x):
# Swin Transformerで人体全体の階層的特徴を抽出
x = self.model.forward_features(x)
# Global Average Pooling
if x.dim() == 3:
x = self.avgpool1d(x.permute((0, 2, 1)))
else:
x = self.avgpool2d(x.permute((0, 3, 1, 2)))
x = x.view(x.size(0), x.size(1))
# 512次元の人体特徴ベクトルを生成(BNNeck経由)
x = self.classifier(x)
return x
def download_pretrained_model():
"""Layumi's ReID Baseline学習済みモデルのダウンロード"""
model_path = 'layumi_swin_pretrained.pth'
if not os.path.exists(model_path):
if not GDOWN_AVAILABLE:
print("gdownが利用できないため、学習済みモデルをダウンロードできません。")
print("pip install gdownを実行してください。")
return None
print("Layumi's ReID Baseline (Market-1501学習済み) モデルをダウンロード中...")
try:
file_id = '1XVEYb0TN2SbBYOqf8SzazfYZlpH9CxyE'
gdown.download(f'https://drive.google.com/uc?id={file_id}', model_path, quiet=False)
print("ダウンロード完了(751人分の人体特徴を学習済み)")
except Exception as e:
print(f"ダウンロードエラー: {e}")
print("ImageNet事前学習のみで実行します")
return None
return model_path
# Layumi's Person Re-IDモデルの初期化(751人分の人体ID分類)
model = ft_net_swin(class_num=751, linear_num=512, circle=False).to(device)
# Layumi's ReID Baseline学習済みモデルのロード
pretrained_path = download_pretrained_model()
if pretrained_path and os.path.exists(pretrained_path):
try:
checkpoint = torch.load(pretrained_path, map_location=device)
model.load_state_dict(checkpoint, strict=False)
print("Layumi's ReID Baseline (Market-1501) モデルをロード完了")
print("学習内容: 服装、体型、持ち物、姿勢などの人体全体特徴")
except Exception as e:
print(f"モデルロードエラー: {e}")
print("ImageNet事前学習のみで実行")
model.eval()
# 入力画像を224x224にリサイズして正規化
transform = transforms.Compose([
transforms.Resize((224, 224), interpolation=3),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
def calculate_cosine_similarity(feat1, feat2):
"""コサイン類似度の計算
定義: cos(θ) = (A・B) / (||A|| × ||B||)
- A・B: ベクトルAとBの内積
- ||A||, ||B||: ベクトルAとBのL2ノルム(ユークリッドノルム)
値域: [-1, 1]
- 1: 完全に同一方向(同一特徴)
- 0: 直交(無関係)
- -1: 完全に逆方向(正反対の特徴)
Person Re-IDでの解釈:
- 0.9以上: ほぼ同一人物の可能性が高い
- 0.7-0.9: 類似した外観(同一人物の可能性あり)
- 0.5-0.7: 部分的に類似
- 0.5未満: 異なる人物の可能性が高い
参考: Hermans, A., Beyer, L., & Leibe, B. (2017).
In defense of the triplet loss for person re-identification.
"""
feat1_norm = feat1 / np.linalg.norm(feat1)
feat2_norm = feat2 / np.linalg.norm(feat2)
return np.dot(feat1_norm, feat2_norm)
def calculate_euclidean_distance(feat1, feat2):
"""ユークリッド距離の計算
定義: d = √(Σ(ai - bi)²)
Person Re-IDでの使用:
特徴空間での距離を測定。小さいほど類似度が高い。
"""
return np.linalg.norm(feat1 - feat2)
def draw_japanese_text(img, text, position, font, color=(0, 255, 0)):
"""OpenCV画像に日本語テキストを描画"""
if font_japanese is None:
# フォントが利用できない場合は英語で描画
cv2.putText(img, text, position, cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2)
return img
# PILで日本語描画
img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(img_pil)
draw.text(position, text, font=font, fill=color[::-1]) # BGRをRGBに変換
return cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)
def video_frame_processing(frame):
global frame_count, previous_feature, feature_vectors_log, previous_frame_number
current_time = time.time()
frame_count += 1
try:
# フレーム全体をPIL画像に変換(本来は人体検出後の画像を使用すべき)
pil_image = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
input_tensor = transform(pil_image).unsqueeze(0).to(device)
# Layumi's ReID Baselineで人体特徴ベクトルを抽出
with torch.no_grad():
outputs = model(input_tensor)
if isinstance(outputs, list):
features = outputs[1] # circle=Trueの場合
else:
features = outputs
# 512次元の人体特徴ベクトル(BNNeck後)
feature_vector = features.cpu().numpy().flatten()
# 現在のフレームの512次元ベクトルの統計情報
feat_mean = np.mean(feature_vector) # 現在のフレームの512次元の平均
feat_std = np.std(feature_vector) # 現在のフレームの512次元の標準偏差
feat_min = np.min(feature_vector) # 現在のフレームの512次元の最小値
feat_max = np.max(feature_vector) # 現在のフレームの512次元の最大値
feat_norm = np.linalg.norm(feature_vector) # 現在のフレームの512次元のL2ノルム
# 前フレームとの比較
comparison_text = "比較対象: なし(初回フレーム)"
appearance_change = "計測開始"
cosine_sim = None # 初回は比較不可
euclidean_dist = None # 初回は比較不可
if previous_feature is not None and previous_frame_number is not None:
# 現在のフレームと直前のフレームの比較
cosine_sim = calculate_cosine_similarity(feature_vector, previous_feature)
euclidean_dist = calculate_euclidean_distance(feature_vector, previous_feature)
comparison_text = f"比較対象: フレーム{frame_count} vs フレーム{previous_frame_number}"
# 外観変化の判定(コサイン類似度に基づく)
if cosine_sim > 0.95:
appearance_change = "同一外観"
elif cosine_sim > 0.85:
appearance_change = "微小変化"
elif cosine_sim > 0.70:
appearance_change = "中程度変化"
elif cosine_sim > 0.50:
appearance_change = "大幅変化"
else:
appearance_change = "完全に異なる"
# 特徴ベクトルをログに保存
feature_data = {
'frame': frame_count,
'timestamp': current_time,
'feature_vector': feature_vector.tolist(),
'statistics': {
'mean': float(feat_mean),
'std': float(feat_std),
'min': float(feat_min),
'max': float(feat_max),
'l2_norm': float(feat_norm),
'description': '現在のフレームの512次元ベクトルの統計値'
},
'comparison': {
'compared_with_frame': previous_frame_number if previous_frame_number else None,
'cosine': float(cosine_sim) if cosine_sim is not None else None,
'euclidean_distance': float(euclidean_dist) if euclidean_dist is not None else None,
'description': comparison_text
}
}
feature_vectors_log.append(feature_data)
# 現在のフレームの特徴を次回比較用に保存
previous_feature = feature_vector.copy()
previous_frame_number = frame_count
if cosine_sim is not None and euclidean_dist is not None:
result = f"フレーム{frame_count}: {comparison_text}, コサイン類似度={cosine_sim:.4f}, ユークリッド距離={euclidean_dist:.3f}, {appearance_change}"
else:
result = f"フレーム{frame_count}: {comparison_text}, {appearance_change}"
# 画面表示(日本語対応)
frame = draw_japanese_text(frame, "人物再識別システム", (10, 30), font_japanese, (0, 255, 0))
frame = draw_japanese_text(frame, f"現在のフレーム: {frame_count}", (10, 60), font_japanese, (0, 255, 0))
frame = draw_japanese_text(frame, comparison_text, (10, 90), font_japanese, (0, 255, 0))
if cosine_sim is not None:
frame = draw_japanese_text(frame, f"コサイン類似度: {cosine_sim:.4f}", (10, 120), font_japanese, (0, 255, 0))
if euclidean_dist is not None:
frame = draw_japanese_text(frame, f"ユークリッド距離: {euclidean_dist:.3f}", (10, 150), font_japanese, (0, 255, 0))
frame = draw_japanese_text(frame, f"状態: {appearance_change}", (10, 180), font_japanese, (0, 255, 0))
frame = draw_japanese_text(frame, f"512次元統計: 平均={feat_mean:.3f}, 標準偏差={feat_std:.3f}", (10, 210), font_japanese, (0, 255, 0))
return frame, result, current_time
except Exception as e:
result = f"エラー: {str(e)}"
return frame, result, current_time
print("\n" + "="*70)
print("Layumi's Person Re-ID Baseline with Swin Transformer")
print("GitHub: https://github.com/layumi/Person_reID_baseline_pytorch")
print("="*70)
print("\n【比較方法】")
print("比較対象: 現在のフレーム(N) と 直前のフレーム(N-1) の512次元特徴ベクトル")
print("比較タイミング: 各フレーム処理時に直前フレームと比較")
print("\n【類似度メトリクスの定義】")
print("1. コサイン類似度 (Cosine Similarity):")
print(" 定義: cos(θ) = (A・B) / (||A|| × ||B||)")
print(" 比較対象: フレームNの512次元ベクトル と フレームN-1の512次元ベクトル")
print(" 値域: [-1, 1] (1=完全一致, 0=無関係, -1=正反対)")
print(" Person Re-IDでの解釈:")
print(" > 0.95: ほぼ同一人物・同一姿勢")
print(" 0.85-0.95: 同一人物の可能性高(姿勢変化)")
print(" 0.70-0.85: 類似外観(同一人物の可能性あり)")
print(" 0.50-0.70: 部分的類似")
print(" < 0.50: 異なる人物の可能性大")
print("\n2. ユークリッド距離 (Euclidean Distance):")
print(" 定義: d = √(Σ(ai - bi)²)")
print(" 比較対象: フレームNの512次元ベクトル と フレームN-1の512次元ベクトル")
print(" 値域: [0, ∞) (0=完全一致, 大きいほど相違)")
print("\n【統計値の対象】")
print("平均・標準偏差・最小値・最大値・L2ノルム:")
print(" 対象: 各フレームの512次元特徴ベクトル(そのフレーム内の512個の値)")
print(" 注意: 全フレームの統計ではなく、個別フレームの512次元の統計")
print("\n【重要】このモデルについて:")
print(" - 実装: Layumi's PyTorch ReID Baseline")
print(" - 対象: 人体全身(顔ではありません)")
print(" - 学習データ: Market-1501 (1501人の歩行者画像)")
print(" - 認識内容: 服装、体型、持ち物、姿勢などの外観特徴")
print(" - 特徴ベクトル: 512次元(BNNeck後の特徴表現)")
print(" - 注意: 現在は画像全体を処理(本来は人体検出が必要)")
print("\n" + "="*70)
print("\n入力選択:")
print("0: 動画ファイル")
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 = "人物再識別 (Person Re-ID)"
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('\n')
f.write('\n'.join(results_log))
print(f'\n処理結果をresult.txtに保存しました')
# 特徴ベクトルログの保存
if feature_vectors_log:
with open('feature_vectors.json', 'w', encoding='utf-8') as f:
json.dump(feature_vectors_log, f, ensure_ascii=False, indent=2)
print(f'特徴ベクトルをfeature_vectors.jsonに保存しました')