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

その処理、GPUじゃなくて勝手にCPUで実行されてるかも  ~ONNX RuntimeのcuDNN 警告と対策~

その処理、GPUじゃなくて勝手にCPUで実行されてるかも ~ONNX RuntimeのcuDNN 警告と対策~

こんにちは! 本日は、ONNX RuntimeでGPU推論時の「libcudnn.so.9: cannot open shared object file」エラーの解決方法についての内容となります。 ONNX Runtimeを使用してGPU推論を行う際、CUDAプロバイダの初期化エラーに遭遇することがありますので、このエラーの原因と解決方法を解説いたします。 エラーメッセージの詳細 [E:onnxruntime:Default, provider_bridge_ort.cc:2195 TryGetProviderInfo_CUDA] /onnxruntime_src/onnxruntime/core/session/provider_bridge_ort.cc:1778 onnxruntime::Provider& onnxruntime::ProviderLibrary::Get() [ONNXRuntimeError] : 1 : FAIL : Failed to load

By Qualiteg プロダクト開発部
大企業のAIセキュリティを支える基盤技術 - 今こそ理解するActive Directory 第3回 クライアントとサーバーのドメイン参加

大企業のAIセキュリティを支える基盤技術 - 今こそ理解するActive Directory 第3回 クライアントとサーバーのドメイン参加

こんにちは、今回はシリーズ第3回クライアントとサーバーのドメイン参加について解説いたします! はじめに こんにちは!シリーズ第3回「クライアントとサーバーのドメイン参加」へようこそ。 前回(第2回)では、Active Directoryドメイン環境の構築手順について、ドメインコントローラーのセットアップからDNS設定まで詳しく解説しました。ドメイン環境の「土台」が整ったところで、今回はいよいよ実際にコンピューターをドメインに参加させる手順に進みます。 「ドメインユーザーアカウントを作ったのに、なぜかログインできない」「新しいPCを追加したけど、ドメイン認証が使えない」といった経験はありませんか?実は、Active Directoryの世界では、ユーザーアカウントを作成しただけでは不十分で、そのユーザーが使用するコンピューター自体もドメインに「参加」させる必要があるのです。 本記事では、このドメイン参加について、単なる手順の説明にとどまらず、「なぜドメイン参加が必要なのか」「裏側で何が起きているのか」という本質的な仕組みまで、初心者の方にも分かりやすく解説していきます。Win

By Qualiteg コンサルティング
使い捨てソフトウェア時代の幕開け ― 市場構造の根本的変革と日本企業

使い捨てソフトウェア時代の幕開け ― 市場構造の根本的変革と日本企業

こんにちは、株式会社Qualiteg コンサルティング部門です。 昨今、生成AIの急速な進化により、ソフトウェア開発の在り方が根本から変わりつつあります。2024年にはClaude、GPT-4、Geminiなどの大規模言語モデルがコード生成能力を飛躍的に向上させ、GitHub CopilotやCursor、Windsurf等の開発支援ツールが実際の開発現場で広く活用されるようになりました。さらに、Devin、OpenAI Canvas、Anthropic Claude Codingといった、より高度な自律的コーディング機能を持つAIエージェントも登場しています。 このような技術革新を背景に、当部門では今後のソフトウェア産業の構造変化について詳細な分析を行いました。本シリーズでは、特に注目すべき変化として、従来1000人月規模を要していた企業向けSaaSプラットフォームや、基幹システムが、AIエージェントを効果的に活用することで、わずか2-3名のチームが数日から数週間で実装可能になるという、開発生産性の劇的な向上について考察してまいります。 これは単なる効率化ではなく、ソフトウェア

By Qualiteg コンサルティング
NVIDIA GeForce RTX 50xx with CUDA capability sm_120 is not compatible with the current PyTorch installation. が発生したとき

NVIDIA GeForce RTX 50xx with CUDA capability sm_120 is not compatible with the current PyTorch installation. が発生したとき

こんにちは、PyTorch 2.6.0 環境で以下のような問題が発生したときの対処方法について解説いたします。 NVIDIA GeForce RTX 5090 with CUDA capability sm_120 is not compatible with the current PyTorch installation. The current PyTorch install supports CUDA capabilities sm_50 sm_60 sm_70 sm_75 sm_80 sm_86 sm_90. 他のBlackwell GeForce の場合は以下のようなメッセージとなります。 NVIDIA GeForce RTX

By Qualiteg プロダクト開発部