ストーリー
田
田中VPoE
VLMの仕組みを理解したな。次は各モダリティのデータをAIに渡す前の「前処理」と「特徴抽出」について学ぼう
あなた
生の画像や音声をそのままAPIに投げるだけではダメなんですか?
あ
田
田中VPoE
そのまま投げることもできるが、前処理を適切に行うことで精度が大きく向上する。画像のリサイズ、音声のノイズ除去、動画のフレーム抽出など、各モダリティには定番のパイプラインがある
あなた
なるほど。モダリティごとに最適な処理の流れがあるんですね
あ
田
田中VPoE
そうだ。NetShop社で扱う画像・音声・動画それぞれのパイプラインを設計できるようになろう
画像処理パイプライン
基本的な処理フロー
画像入力 → 前処理 → 特徴抽出 → AI処理 → 後処理 → 出力
前処理の詳細
from PIL import Image
import io
def preprocess_image(image_path: str, target_size: int = 1024) -> bytes:
"""画像の前処理パイプライン"""
img = Image.open(image_path)
# 1. EXIFに基づく回転補正
from PIL import ImageOps
img = ImageOps.exif_transpose(img)
# 2. カラーモード正規化(RGBA → RGB)
if img.mode == "RGBA":
background = Image.new("RGB", img.size, (255, 255, 255))
background.paste(img, mask=img.split()[3])
img = background
elif img.mode != "RGB":
img = img.convert("RGB")
# 3. リサイズ(アスペクト比維持)
width, height = img.size
if max(width, height) > target_size:
ratio = target_size / max(width, height)
new_size = (int(width * ratio), int(height * ratio))
img = img.resize(new_size, Image.LANCZOS)
# 4. 品質を指定してバイト列に変換
buffer = io.BytesIO()
img.save(buffer, format="JPEG", quality=85)
return buffer.getvalue()
画像処理のベストプラクティス
| 項目 | 推奨 | 理由 |
|---|
| 解像度 | 長辺1024〜2048px | APIのトークン消費と精度のバランス |
| フォーマット | JPEG (写真), PNG (図表) | 用途に応じた選択 |
| ファイルサイズ | 5MB以下 | APIの制限とレイテンシ |
| カラーモード | RGB | モデルの標準入力 |
音声処理パイプライン
基本的な処理フロー
音声入力 → ノイズ除去 → 正規化 → セグメント化 → STT → テキスト後処理
前処理の詳細
import numpy as np
from pydub import AudioSegment
def preprocess_audio(audio_path: str) -> str:
"""音声の前処理パイプライン"""
# 1. 音声ファイルの読み込み
audio = AudioSegment.from_file(audio_path)
# 2. モノラル変換
audio = audio.set_channels(1)
# 3. サンプルレート正規化(16kHz: STT標準)
audio = audio.set_frame_rate(16000)
# 4. 音量正規化(-20 dBFS目標)
target_dbfs = -20
change_in_dbfs = target_dbfs - audio.dBFS
audio = audio.apply_gain(change_in_dbfs)
# 5. 無音部分の除去(先頭・末尾)
from pydub.silence import detect_leading_silence
start_trim = detect_leading_silence(audio, silence_threshold=-40)
end_trim = detect_leading_silence(audio.reverse(), silence_threshold=-40)
audio = audio[start_trim:len(audio) - end_trim]
# 6. 出力
output_path = audio_path.rsplit(".", 1)[0] + "_processed.wav"
audio.export(output_path, format="wav")
return output_path
def split_audio_segments(audio_path: str, max_duration_ms: int = 600000) -> list[str]:
"""長時間音声のセグメント分割(Whisper APIの制限: 25MB)"""
audio = AudioSegment.from_file(audio_path)
segments = []
for i in range(0, len(audio), max_duration_ms):
segment = audio[i:i + max_duration_ms]
segment_path = f"{audio_path}_segment_{i // max_duration_ms}.wav"
segment.export(segment_path, format="wav")
segments.append(segment_path)
return segments
音声処理のベストプラクティス
| 項目 | 推奨 | 理由 |
|---|
| サンプルレート | 16kHz | STTモデルの標準 |
| チャンネル | モノラル | STTでは不要なステレオ情報を削除 |
| フォーマット | WAV / FLAC | ロスレス形式で精度を確保 |
| セグメント長 | 10分以内 | APIの制限とメモリ効率 |
動画処理パイプライン
基本的な処理フロー
動画入力 → フレーム抽出 → キーフレーム選定 → 画像処理 → AI分析
→ 音声分離 → 音声処理 → STT → テキスト統合
フレーム抽出
import cv2
from typing import Generator
def extract_keyframes(
video_path: str,
interval_seconds: float = 5.0,
max_frames: int = 50
) -> Generator[tuple[float, bytes], None, None]:
"""動画からキーフレームを等間隔で抽出"""
cap = cv2.VideoCapture(video_path)
fps = cap.get(cv2.CAP_PROP_FPS)
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
interval_frames = int(fps * interval_seconds)
frame_count = 0
extracted = 0
while cap.isOpened() and extracted < max_frames:
ret, frame = cap.read()
if not ret:
break
if frame_count % interval_frames == 0:
timestamp = frame_count / fps
_, buffer = cv2.imencode(".jpg", frame)
yield (timestamp, buffer.tobytes())
extracted += 1
frame_count += 1
cap.release()
def extract_audio_from_video(video_path: str, output_path: str) -> str:
"""動画から音声トラックを分離"""
from moviepy.editor import VideoFileClip
video = VideoFileClip(video_path)
video.audio.write_audiofile(output_path)
video.close()
return output_path
動画処理の考慮事項
| 項目 | 推奨 | 理由 |
|---|
| フレーム間隔 | 3〜10秒 | シーン変化の捕捉とコストのバランス |
| 最大フレーム数 | 50〜100枚 | API呼び出しコストの制御 |
| キーフレーム選定 | シーン変化検出を併用 | 重要な場面を逃さない |
| 音声分離 | 必ず実施 | 音声とフレームの両方を分析 |
モダリティ間の連携パターン
パターン1: シーケンシャル処理
音声 → [STT] → テキスト → [LLM] → 要約
パターン2: パラレル処理
動画 → [フレーム抽出] → 画像分析 ─┐
→ [音声分離] → STT ─┤→ [統合] → 最終出力
│
メタデータ ────────────────────────┘
パターン3: カスケード処理
スキャン文書 → [OCR] → テキスト → [NER] → 固有表現 → [構造化] → JSON
まとめ
| モダリティ | 前処理のポイント | 主要ツール |
|---|
| 画像 | リサイズ、回転補正、カラー正規化 | Pillow, OpenCV |
| 音声 | ノイズ除去、正規化、セグメント化 | pydub, librosa |
| 動画 | フレーム抽出、音声分離 | OpenCV, moviepy |
チェックリスト
推定所要時間: 30分