LESSON 30分

ストーリー

田中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〜2048pxAPIのトークン消費と精度のバランス
フォーマット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

音声処理のベストプラクティス

項目推奨理由
サンプルレート16kHzSTTモデルの標準
チャンネルモノラル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分