computer-vision-pipeline について
computer-vision-pipelineは、物体検出、動画解析、マルチオブジェクト追跡のシステム構築を支援するスキルです。フレームの前処理や抽出、バッチ推論、モデルのファインチューニング、追跡手法を扱います。
主な機能
- 01物体検出モデルの選定ガイド
- 02動画フレームの前処理とサンプリング
- 03バッチ推論とNMS調整の説明
- 04ByteTrackなどによるマルチオブジェクト追跡
- 05YOLOの調整と動画解析スクリプト
活用シーン
- 01ドローン映像や野生動物の動画解析
- 02リアルタイム物体検出パイプラインの構築
- 03動画フレーム間の複数物体の追跡
- 04カスタムデータセットでYOLOを調整
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ curiositech/ some_claude_skills --skill computer-vision-pipelineインストール後、Claude Code で/computer-vision-pipelineを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
.claude-plugin/plugin.jsonreferences/yolo-guide.mdvideo-processing.mdtracking-algorithms.mdscripts/model_trainer.pyvideo_analyzer.py詳細
コンピュータービジョンパイプライン
物体検出、追跡、動画解析のための、本番環境ですぐに使えるコンピュータービジョンシステムの構築に精通しています。
使用する場面
✅ 以下の場合に使用:
- ドローン映像の解析(考古学調査、自然保護)
- 野生動物のモニタリングと追跡
- リアルタイム物体検出システム
- 動画の前処理と解析
- カスタムモデルのトレーニングと推論
- マルチオブジェクトトラッキング(MOT)
❌ 以下の場合には使用しない:
- シンプルな画像フィルター(Pillow/PIL を使用)
- 写真編集(Photoshop/GIMP を使用)
- 顔認識 API(AWS Rekognition を使用)
- 基本的な OCR(Tesseract を使用)
テクノロジーの選定
物体検出モデル
| モデル | 速度(FPS) | 精度(mAP) | 用途 |
|---|---|---|---|
| YOLOv8 | 140 | 53.9% | リアルタイム検出 |
| Detectron2 | 25 | 58.7% | 高精度、研究 |
| EfficientDet | 35 | 55.1% | モバイルへのデプロイ |
| Faster R-CNN | 10 | 42.0% | レガシーシステム |
タイムライン:
- 2015:Faster R-CNN(2段階検出)
- 2016:YOLO v1(1段階、リアルタイム)
- 2020:YOLOv5(PyTorch、本番環境対応)
- 2023:YOLOv8(最先端)
- 2024:YOLOv8 はリアルタイム検出の業界標準
判断ツリー:
Need real-time (>30 FPS)? → YOLOv8
Need highest accuracy? → Detectron2 Mask R-CNN
Need mobile deployment? → YOLOv8-nano or EfficientDet
Need instance segmentation? → Detectron2 or YOLOv8-seg
Need custom objects? → Fine-tune YOLOv8
よくあるアンチパターン
アンチパターン 1:検出前にフレームを前処理しない
初心者の考え方:「未加工の動画フレームにそのまま検出を実行すればよい」
問題:検出精度が低く、GPU サイクルが無駄になります。
誤った方法:
# ❌ No preprocessing - poor results
import cv2
from ultralytics import YOLO
model = YOLO('yolov8n.pt')
video = cv2.VideoCapture('drone_footage.mp4')
while True:
ret, frame = video.read()
if not ret:
break
# Raw frame detection - no normalization, no resizing
results = model(frame)
# Poor accuracy, slow inference
誤りである理由:
- 動画の解像度が高すぎます(4K は1フレームあたり8.3メガピクセル)
- 正規化されていません(ピクセル値が0~1ではなく0~255)
- アスペクト比が維持されていません
- 高解像度フレームで GPU メモリがオーバーフローします
正しい方法:
# ✅ Proper preprocessing pipeline
import cv2
import numpy as np
from ultralytics import YOLO
model = YOLO('yolov8n.pt')
video = cv2.VideoCapture('drone_footage.mp4')
# Model expects 640x640 input
TARGET_SIZE = 640
def preprocess_frame(frame):
# Resize while maintaining aspect ratio
h, w = frame.shape[:2]
scale = TARGET_SIZE / max(h, w)
new_w, new_h = int(w * scale), int(h * scale)
resized = cv2.resize(frame, (new_w, new_h), interpolation=cv2.INTER_LINEAR)
# Pad to square
pad_w = (TARGET_SIZE - new_w) // 2
pad_h = (TARGET_SIZE - new_h) // 2
padded = cv2.copyMakeBorder(
resized,
pad_h, TARGET_SIZE - new_h - pad_h,
pad_w, TARGET_SIZE - new_w - pad_w,
cv2.BORDER_CONSTANT,
value=(114, 114, 114) # Gray padding
)
# Normalize to 0-1 (if model expects it)
# normalized = padded.astype(np.float32) / 255.0
return padded, scale
while True:
ret, frame = video.read()
if not ret:
break
preprocessed, scale = preprocess_frame(frame)
results = model(preprocessed)
# Scale bounding boxes back to original coordinates
for box in results[0].boxes:
x1, y1, x2, y2 = box.xyxy[0]
x1, y1, x2, y2 = x1/scale, y1/scale, x2/scale, y2/scale
パフォーマンスの比較:
- 未加工の4Kフレーム:5 FPS、72% mAP
- 前処理済み640x640:45 FPS、89% mAP
タイムラインの背景:
- 2015:手動での前処理が必要
- 2020:YOLOv5 に自動リサイズが追加
- 2023:YOLOv8 はスマートな前処理に対応していますが、明示的に制御するほうが効果的です
アンチパターン 2:動画のすべてのフレームを処理する
初心者の考え方:「1フレーム残らず検出を実行すればよい」
問題:フレームの99%は重複しており、計算リソースが無駄になります。
誤った方法:
# ❌ Process every frame (30 FPS video = 1800 frames/min)
import cv2
from ultralytics import YOLO
model = YOLO('yolov8n.pt')
video = cv2.VideoCapture('drone_footage.mp4')
detections = []
while True:
ret, frame = video.read()
if not ret:
break
# Run detection on EVERY frame
results = model(frame)
detections.append(results)
# 10-minute video = 18,000 inferences (15 minutes on GPU)
誤りである理由:
- 隣接するフレームはほぼ同一です
- 重複した処理に計算リソースの95%を浪費します
- 処理時間が長くなります
- 結果の保存に膨大なストレージが必要です
正しい方法 1:フレームのサンプリング
# ✅ Sample every Nth frame
import cv2
from ultralytics import YOLO
model = YOLO('yolov8n.pt')
video = cv2.VideoCapture('drone_footage.mp4')
SAMPLE_RATE = 30 # Process 1 frame per second (if 30 FPS video)
frame_count = 0
detections = []
while True:
ret, frame = video.read()
if not ret:
break
frame_count += 1
# Only process every 30th frame
if frame_count % SAMPLE_RATE == 0:
results = model(frame)
detections.append({
'frame': frame_count,
'timestamp': frame_count / 30.0,
'results': results
})
# 10-minute video = 600 inferences (30 seconds on GPU)
正しい方法 2:シーン変化検出を使った適応型サンプリング
# ✅ Only process when scene changes significantly
import cv2
import numpy as np
from ultralytics import YOLO
model = YOLO('yolov8n.pt')
video = cv2.VideoCapture('drone_footage.mp4')
def scene_changed(prev_frame, curr_frame, threshold=0.3):
"""Detect scene change using histogram comparison"""
if prev_frame is None:
return True
# Convert to grayscale
prev_gray = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY)
curr_gray = cv2.cvtColor(curr_frame, cv2.COLOR_BGR2GRAY)
# Calculate histograms
prev_hist = cv2.calcHist([prev_gray], [0], None, [256], [0, 256])
curr_hist = cv2.calcHist([curr_gray], [0], None, [256], [0, 256])
# Compare histograms
correlation = cv2.compareHist(prev_hist, curr_hist, cv2.HISTCMP_CORREL)
return correlation < (1 - threshold)
prev_frame = None
detections = []
while True:
ret, frame = video.read()
if not ret:
break
# Only run detection if scene changed
if scene_changed(prev_frame, frame):
results = model(frame)
detections.append(results)
prev_frame = frame.copy()
# Adapts to video content - static shots skip frames, action scenes process more
削減効果:
- すべてのフレーム:18,000回の推論
- 1 FPSでサンプリング:600回の推論(97%削減)
- 適応型:約1,200回の推論(93%削減)
アンチパターン 3:バッチ推論を使用しない
初心者の考え方:「画像を1枚ずつ処理すればよい」
問題:データを待つ間、GPU は時間の80%をアイドル状態で過ごします。
誤った方法:
# ❌ Sequential processing - GPU underutilized
import cv2
from ultralytics import YOLO
import time
model = YOLO('yolov8n.pt')
# 100 images to process
image_paths = [f'frame_{i:04d}.jpg' for i in range(100)]
start = time.time()
for path in image_paths:
frame = cv2.imread(path)
results = model(frame) # Process one at a time
# GPU utilization: ~20%
elapsed = time.time() - start
print(f"Processed {len(image_paths)} images in {elapsed:.2f}s")
# Output: 45 seconds
誤りである理由:
- GPU は画像を1枚ずつ読み込む CPU を待たなければなりません
- 並列化されていません
- GPU 使用率が約20%です
- スループットが低下します
正しい方法:
# ✅ Batch inference - GPU fully utilized
import cv2
from ultralytics import YOLO
import time
model = YOLO('yolov8n.pt')
image_paths = [f'frame_{i:04d}.jpg' for i in range(100)]
BATCH_SIZE = 16 # Process 16 images at once
start = time.time()
for i in range(0, len(image_paths), BATCH_SIZE):
batch_paths = image_paths[i:i+BATCH_SIZE]
# Load batch
frames = [cv2.imread(path) for path in batch_paths]
# Batch inference (single GPU call)
results = model(frames) # Pass list of images
# GPU utilization: ~85%
elapsed = time.time() - start
print(f"Processed {len(image_paths)} images in {elapsed:.2f}s")
# Output: 8 seconds (5.6x faster!)
パフォーマンスの比較:
| 方法 | 所要時間(画像100枚) | GPU 使用率 | スループット |
|---|---|---|---|
| 順次処理 | 45秒 | 20% | 2.2 img/s |
| バッチ(16) | 8秒 | 85% | 12.5 img/s |
| バッチ(32) | 6秒 | 92% | 16.7 img/s |
バッチサイズの調整:
# Find optimal batch size for your GPU
import torch
def find_optimal_batch_size(model, image_size=(640, 640)):
for batch_size in [1, 2, 4, 8, 16, 32, 64]:
try:
dummy_input = torch.randn(batch_size, 3, *image_size).cuda()
start = time.time()
with torch.no_grad():
_ = model(dummy_input)
elapsed = time.time() - start
throughput = batch_size / elapsed
print(f"Batch {batch_size}: {throughput:.1f} img/s")
except RuntimeError as e:
print(f"Batch {batch_size}: OOM (out of memory)")
break
# Find optimal batch size before production
find_optimal_batch_size(model)
アンチパターン 4:非極大値抑制(NMS)の調整を無視する
問題:重複検出、物体の見落とし、後処理の遅延が発生します。
誤った方法:
# ❌ Use default NMS settings for everything
from ultralytics import YOLO
model = YOLO('yolov8n.pt')
# Default settings (iou_threshold=0.45, conf_threshold=0.25)
results = model('crowded_scene.jpg')
# Result: 50 bounding boxes, 30 are duplicates!
誤りである理由:
- デフォルトの IoU=0.45 は、物体が密集している場合には許容範囲が広すぎます
- デフォルトの conf=0.25 では、品質の低い検出も含まれます
- 用途に応じた調整がありません
正しい方法:
# ✅ Tune NMS for your use case
from ultralytics import YOLO
model = YOLO('yolov8n.pt')
# Sparse objects (dolphins in ocean)
sparse_results = model(
'ocean_footage.jpg',
iou=0.5, # Higher IoU = allow closer boxes
conf=0.4 # Higher confidence = fewer false positives
)
# Dense objects (crowd, flock of birds)
dense_results = model(
'crowded_scene.jpg',
iou=0.3, # Lower IoU = suppress more duplicates
conf=0.5 # Higher confidence = filter noise
)
# High precision needed (legal evidence)
precise_results = model(
'evidence.jpg',
iou=0.5,
conf=0.7, # Very high confidence
max_det=50 # Limit max detections
)
NMS パラメーターガイド:
| 用途 | IoU | Conf | 最大検出数 |
|---|---|---|---|
| 物体がまばらな場合(野生動物) | 0.5 | 0.4 | 100 |
| 物体が密集している場合(群衆) | 0.3 | 0.5 | 300 |
| 高精度が必要な場合(証拠) | 0.5 | 0.7 | 50 |
| リアルタイム(速度優先) | 0.45 | 0.3 | 100 |
反パターン 5:フレーム間の追跡がない
初学者の考え:「各フレームで個別に検出を実行します」
問題点:個々の物体を数えたり、動きを追跡したり、軌跡を作成したりできません。
誤った方法:
# ❌ Independent frame detection - no object identity
from ultralytics import YOLO
import cv2
model = YOLO('yolov8n.pt')
video = cv2.VideoCapture('dolphins.mp4')
detections = []
while True:
ret, frame = video.read()
if not ret:
break
results = model(frame)
detections.append(results)
# Result: Can't tell if frame 10 dolphin is same as frame 20 dolphin
# Can't count unique dolphins
# Can't track trajectories
誤りである理由:
- フレーム間で物体の同一性を保持できません
- 個々の物体を数えられません
- 動きのパターンを分析できません
- 軌跡を作成できません
正しい方法:追跡(ByteTrack)を使用します
# ✅ Multi-object tracking with ByteTrack
from ultralytics import YOLO
import cv2
# YOLO with tracking
model = YOLO('yolov8n.pt')
video = cv2.VideoCapture('dolphins.mp4')
# Track objects across frames
tracks = {}
while True:
ret, frame = video.read()
if not ret:
break
# Run detection + tracking
results = model.track(
frame,
persist=True, # Maintain IDs across frames
tracker='bytetrack.yaml' # ByteTrack algorithm
)
# Each detection now has persistent ID
for box in results[0].boxes:
track_id = int(box.id[0]) # Unique ID across frames
x1, y1, x2, y2 = box.xyxy[0]
# Store trajectory
if track_id not in tracks:
tracks[track_id] = []
tracks[track_id].append({
'frame': len(tracks[track_id]),
'bbox': (x1, y1, x2, y2),
'conf': box.conf[0]
})
# Now we can analyze:
print(f"Unique dolphins detected: {len(tracks)}")
# Trajectory analysis
for track_id, trajectory in tracks.items():
if len(trajectory) > 30: # Only long tracks
print(f"Dolphin {track_id} appeared in {len(trajectory)} frames")
# Calculate movement, speed, etc.
追跡の利点:
- 個々の物体を数えられます(フレームごとの検出数ではありません)
- 軌跡や動きのパターンを作成できます
- 時間経過に伴う動作を分析できます
- 一時的な誤検出を除外できます
追跡アルゴリズム:
| アルゴリズム | 速度 | 堅牢性 | オクルージョンへの対応 |
|---|---|---|---|
| ByteTrack | 高速 | 良好 | 優秀 |
| SORT | 非常に高速 | 普通 | 普通 |
| DeepSORT | 中程度 | 優秀 | 良好 |
| BotSORT | 中程度 | 優秀 | 優秀 |
本番環境向けチェックリスト
□ Preprocess frames (resize, pad, normalize)
□ Sample frames intelligently (1 FPS or scene change detection)
□ Use batch inference (16-32 images per batch)
□ Tune NMS thresholds for your use case
□ Implement tracking if analyzing video
□ Log inference time and GPU utilization
□ Handle edge cases (empty frames, corrupted video)
□ Save results in structured format (JSON, CSV)
□ Visualize detections for debugging
□ Benchmark on representative data
使用する場合と避ける場合
| シナリオ | 適切かどうか |
|---|---|
| 考古学調査のためにドローン映像を分析する | ✅ はい - カスタム物体検出 |
| 動画内の野生動物を追跡する | ✅ はい - 検出 + 追跡 |
| 群衆の人数を数える | ✅ はい - 密集した物体の検出 |
| リアルタイムの防犯カメラ | ✅ はい - YOLOv8 によるリアルタイム処理 |
| 休暇中の写真を整理する | ❌ いいえ - 写真管理アプリを使用します |
| 顔認識によるログイン | ❌ いいえ - AWS Rekognition API を使用します |
| ナンバープレートを読み取る | ❌ いいえ - 専用の OCR を使用します |
参考資料
/references/yolo-guide.md- YOLOv8 のセットアップ、トレーニング、推論のパターン/references/video-processing.md- フレーム抽出、シーン検出、最適化/references/tracking-algorithms.md- ByteTrack、SORT、DeepSORT の比較
スクリプト
scripts/video_analyzer.py- フレームを抽出し、検出を実行してタイムラインを生成しますscripts/model_trainer.py- カスタムデータセットで YOLO をファインチューニングし、重みをエクスポートします
このスキルが対象とする分野:コンピュータービジョン | 物体検出 | 動画分析 | YOLO | 追跡 | ドローン映像 | 野生動物のモニタリング