OpenCV cv2.imwrite で発生する「_img.empty()」エラーと「動画安定化」による解決法

OpenCV cv2.imwrite で発生する「_img.empty()」エラーと「動画安定化」による解決法

こんにちは!

画像処理や動画解析の現場で広く利用されている OpenCV。

しかし実務で動画処理を行っていると、時折以下のようなエラーに遭遇することがあります。

cv2.error: OpenCV(4.11.0) /io/opencv/modules/imgcodecs/src/loadsave.cpp:929:
error: (-215:Assertion failed) !_img.empty() in function 'imwrite'

このエラーは、cv2.imwrite() に渡された画像が空(None またはサイズ0) の場合に発生します。 一見単純に見える問題ですが、背後には「入力動画の不安定さ」や「並列処理の競合」といった要因が潜んでいることが少なくありません。

本記事では、このエラーの発生原因を掘り下げ、実務で効果のある解決策として 「動画の安定化(正規化)」 を紹介します。

TL;DR

OpenCVの動画処理で頻発する !_img.empty() エラーは、入力動画(mp4)の不安定さが根本原因であることがけっこう多い

不安定な動画→安定化するためのffmpegコマンドは

ffmpeg で動画を安定化

ffmpeg -y -fflags +genpts -i INPUT.mp4 \
  -vf "fps=30,format=yuv420p" \
  -c:v libx264 -preset slow -crf 20 -g 90 -sc_threshold 0 \
  -movflags +faststart -an OUTPUT_stable.mp4

最終手段
それでもダメなら、 連番画像に展開してから処理

目次

  • なぜ「空画像」が紛れ込むのか?
  • エラーを防ぐ基本的な工夫
  • 動画を「安定化」してから処理する
  • さらに堅牢にするレシピ
  • 実務で役立つ環境上の注意点
  • デバッグ方法
  • まとめ

なぜ動画に「空画像」が紛れ込むのか?

1. 入力動画の不安定さ

  • 可変フレームレート(VFR) 一部のカメラやスマートフォンは VFR で撮影しており、処理パイプラインによっては「抜けフレーム」や「タイムスタンプ不整合」が発生します。
  • 特殊なピクセルフォーマット 10bit、4:2:2、4:4:4 など、標準的でないフォーマットはライブラリによって扱いが不安定。
  • GOP 構造の揺らぎ I/P/B フレームの参照構造が複雑な動画は、特定フレームだけ取り出しに失敗するケースがあります。

2. 中間処理での破損

  • 一時的に PNG などへ展開して並列処理を行うと、I/O が競合し libpng error: IDAT: CRC error が出ることがあります
  • たとえば、 Windows + WSL 環境で /mnt/c を経由すると、I/O レイテンシが大きく、破損の可能性が増したりします

3. アルゴリズム側の仕様

  • 顔検出やセグメンテーションなどの処理で「対象が見つからない場合に None を返す」実装になっていると、そのまま imwrite に渡されて落ちます。

4. 環境要因

  • ディスクの空き容量不足やメモリ不足による一時的なフレーム生成失敗。
  • 並列度が高すぎて一時ファイルの読み書きが衝突するケース。

エラーを防ぐ基本的な工夫

imwrite 前のチェック

まずは cv2.imwrite を呼ぶ前に、画像が空でないか確認するのが基本です。

if img is None or getattr(img, "size", 0) == 0:
    # 空の場合はスキップやフォールバック処理を行う
    continue
cv2.imwrite(path, img)

安全なラッパー関数の例

リトライやフォールバックコピーを組み合わせるとさらに堅牢になります。

import cv2, os, shutil, time

def safe_imwrite(path, img, retries=2, sleep=0.05):
    if img is None or getattr(img, "size", 0) == 0:
        return False
    for _ in range(retries + 1):
        try:
            if cv2.imwrite(path, img):
                return True
        except cv2.error:
            pass
        time.sleep(sleep)
    return False

def write_with_fallback(out_path, img, src_path_for_fallback=None):
    os.makedirs(os.path.dirname(out_path), exist_ok=True)
    if safe_imwrite(out_path, img):
        return True
    if src_path_for_fallback and os.path.exists(src_path_for_fallback):
        shutil.copy2(src_path_for_fallback, out_path)
        return True
    return False

動画を「安定化」してから処理する

根本的な解決策として有効なのが、

入力動画を事前に正規化(安定化)すること

です。

これがけっこう効きます

具体的には以下のような ffmpeg コマンドを利用します。

標準的な安定化(最初に試すべきレシピ)

ffmpeg -y -fflags +genpts -i INPUT.mp4 \
  -vf "fps=30,format=yuv420p" \
  -c:v libx264 -preset slow -crf 20 -g 90 -sc_threshold 0 \
  -movflags +faststart \
  -an \
  OUTPUT_stable.mp4

オプションの詳細説明

オプション 説明 効果
-fflags +genpts タイムスタンプ生成 欠損したPTSを補完
-vf fps=30 FPS固定 VFR→CFR変換で安定化
-vf format=yuv420p ピクセルフォーマット統一 最も互換性の高い8bit 4:2:0に
-g 90 GOPサイズ指定 30fpsなら3秒ごとにIフレーム
-sc_threshold 0 シーンチェンジ無効化 GOP構造の完全な安定化
-crf 20 品質設定 品質とファイルサイズのバランス
-movflags +faststart メタデータ配置最適化 ストリーミング再生の高速化
-an 音声除去 映像処理に特化

さらに堅牢にするレシピ

I フレームのみ(All-I)

ffmpeg -i INPUT.mp4 \
  -vf "fps=30,format=yuv420p" \
  -c:v libx264 -crf 18 -g 1 \
  -an \
  OUTPUT_allI.mp4

→ すべて I フレーム化するため、ランダムアクセス時の不具合が激減。ただしファイルサイズは増加。

ProRes 422HQ(中間コーデック)

ffmpeg -i INPUT.mp4 \
  -vf "fps=30,format=yuv422p10le" \
  -c:v prores_ks -profile:v 3 \
  -an \
  OUTPUT_prores422hq.mov

→ 編集用途でもよく使われる高品質・高安定コーデック。

MJPEG(軽量で堅牢)

ffmpeg -i INPUT.mp4 \
  -vf "fps=30,format=yuvj420p" \
  -c:v mjpeg -q:v 3 \
  -an \
  OUTPUT_mjpeg.avi

→ I フレームのみ、デコードも軽い。検証・実験用に便利。

画像連番に展開(最強の安定策)

mkdir -p frames
ffmpeg -i INPUT.mp4 -vf "fps=30" -qscale:v 2 frames/%06d.jpg

→ PNG より JPG の方が CRC エラーを避けやすく、大量並列処理に強い。


実務で役立つ環境上の注意点

  • WSL を使う場合 一時ファイルは /mnt/c ではなく /home/... 側(ext4)に置く方が安定。
  • ディスクの空き容量 df -h で確認し、余裕を持たせる。容量不足は破損の温床。
  • 並列度の調整 ワーカー数を減らすと I/O 衝突や PNG CRC エラーを減らせる。
  • "未検出=原画返し"の仕様に統一 処理関数が None を返さず、検出できなければ元のフレームを返す設計にする。

デバッグ方法

問題の切り分けには、どのフレームで失敗しているかを特定することが重要です。

フレーム読み込みの検証

def debug_frame_read(video_path):
    """どのフレームで読み込みに失敗しているか特定"""
    cap = cv2.VideoCapture(video_path)
    frame_count = 0
    failed_frames = []
    
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        if frame is None or frame.size == 0:
            failed_frames.append(frame_count)
            print(f"Frame {frame_count}: Empty or corrupted")
        frame_count += 1
    
    cap.release()
    
    if failed_frames:
        print(f"\n問題のあるフレーム数: {len(failed_frames)}")
        print(f"最初の10個: {failed_frames[:10]}")
    else:
        print(f"全 {frame_count} フレーム正常に読み込み可能")
    
    return failed_frames

動画の整合性チェック

def verify_video_integrity(video_path):
    """動画ファイルの基本情報と整合性を確認"""
    cap = cv2.VideoCapture(video_path)
    
    if not cap.isOpened():
        print(f"エラー: {video_path} を開けません")
        return False
    
    fps = cap.get(cv2.CAP_PROP_FPS)
    frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    
    print(f"動画情報:")
    print(f"  FPS: {fps}")
    print(f"  フレーム数: {frame_count}")
    print(f"  解像度: {width}x{height}")
    
    # 実際に読み込めるフレーム数をカウント
    actual_count = 0
    while True:
        ret, _ = cap.read()
        if not ret:
            break
        actual_count += 1
    
    cap.release()
    
    if actual_count != frame_count:
        print(f"警告: メタデータのフレーム数({frame_count})と")
        print(f"      実際のフレーム数({actual_count})が一致しません")
        return False
    
    return True

まとめ

  • cv2.imwrite の _img.empty() エラーは 空画像が渡された結果 であり、背景には 入力動画の不安定さや I/O の揺らぎ がある。
  • 解決にはの二段構えが効果的。
    1. ガード処理を入れる(空画像をスキップ or フォールバック)
    2. 動画を ffmpeg で安定化する(CFR化・ピクセルフォーマット統一・GOP安定化)
  • さらに堅牢化するなら、All-I / ProRes / MJPEG / 画像連番 といった手段も検討可能。
  • デバッグ時は、問題のあるフレームを特定することで原因の切り分けが容易になる。

こうした工夫により、実務の動画処理パイプラインはより堅牢で信頼性の高いものになります。

なにはともあれ、
まずは後段で泣かないために入力となる動画の品質をあげましょう!

それでは、お読みいただきありがとうございました!

Read more

日本語対応 LLMランキング2026 ~ベンチマーク分析レポート~(10月22日版)

日本語対応 LLMランキング2026 ~ベンチマーク分析レポート~(10月22日版)

まず結論 2026年10月22日時点のLLMランキング 日本語対応LLMランキングの2026年10月22日版です。前置きは後回しにして、まず最新のランキングからお見せします。 フロンティアモデルの最新ランキング TOP20(Epoch Capabilities Index) 順位 モデル 開発元 提供形態 公開日(Epoch AI記載) ECI 1Claude Opus 5.5AnthropicAPI09/22168.8 2GPT-6 AstraOpenAIAPI09/03168.8 3GPT-6.1 SolOpenAIAPI09/29168.4 4Claude Fable 5.1AnthropicAPI09/01166.5 5Gemini 4 ArgonGoogleAPI09/30166.4 6Claude Sonnet 5.5AnthropicAPI09/28166.2

By Qualiteg プロダクト開発部, Qualiteg コンサルティング
(株)Qualiteg、CEATEC 2026 出展レポート

(株)Qualiteg、CEATEC 2026 出展レポート

こんにちは!2026年10月13日(火)から16日(金)までの4日間、幕張メッセで開催された「CEATEC 2026」に、出展してまいりました。 昨年の CEATEC 2025 に続いて今年も出展してまいりました。 (昨年の様子は CEATEC 2025 出展レポート に、今年の出展予告は こちらの記事 です) 今年は主催者発表で、CEATEC 2026 4日間の登録来場者総数が 112,373人。昨年の98,884人を上回る規模になりました。 当社ブースも昨年より多くのお客様にお越しいただき、 ご来訪者さまは昨年の2倍になりました。 このレポートでは、準備日から最終日までを時系列で振り返ります。 各日の公式来場者数も載せておきますので、来年出展される方(と来年の私たち自身の備忘録としても)のお役に立てば幸いです。 目次 * 1. CEATEC 2026 出展概要 * 2. 前日(10月12日)ブース設営 * 3. DAY1(

By Qualiteg ビジネス開発本部 | マーケティング部
CEATEC 2026に出展します!AIアバター動画生成「MotionVox®」の最新版を実体験いただけます

CEATEC 2026に出展します!AIアバター動画生成「MotionVox®」の最新版を実体験いただけます

株式会社Qualitegは、2026年10月13日(火)~16日(金)に幕張メッセで開催される「CEATEC 2026」に出展いたします。今回の出展では、当社が開発したAIアバター動画生成サービス「MotionVox®」の最新版を中心に、動画生成の実演と作例をご紹介いたします。 昨年の CEATEC 2025 に続き、2年連続の出展となります。昨年の会場の様子は 出展レポート にまとめております。 出展概要 * 会期 2026年10月13日(火)~10月16日(金)10:00~17:00 * 会場 幕張メッセ(千葉県千葉市美浜区中瀬2-1) * 出展エリア ネクストジェネレーションパーク * ブース番号 ホール4 4H339 * 入場 無料(来場事前登録制) * CEATEC内特設サイト https://www.ceatec.com/nj/exhibitor_

By Qualiteg プロダクト開発部
ゼロから作るコーディングエージェント【第3回】「止まらなかった」は「進んだ」ではない。空転を数え、思考は切らずに上限だけ置く

ゼロから作るコーディングエージェント【第3回】「止まらなかった」は「進んだ」ではない。空転を数え、思考は切らずに上限だけ置く

300ターン走ってAPIが0本。「モデルの限界」と結論を書いた翌日、記録を読み直すと234ターンがツールを1度も呼んでいませんでした。空転率という指標、1つ塞ぐと次の形に移る空転、思考をオフにすると95点が71点に落ちて時間も縮まなかった話、出力枠を広げたら反対側を数える話を書きます。

By Qualiteg プロダクト開発部