IYUV(I420/YU12)徹底解説:4:2:0 平面フォーマットのメモリ配置と実装ガイド

IYUVとは — 基本から実践まで(詳細解説)

IYUV(通称 I420 / YU12 と同等と扱われることが多い)は、デジタル映像処理で広く使われる「8ビット・YUV 4:2:0 平面(planar)フォーマット」の一種です。テレビ映像やビデオコーデック、WebRTC やカメラ入出力、ソフトウェアの色変換処理など、多くの場面で見かけるフォーマットなので、構造・扱い方・注意点を理解しておくことは非常に重要です。

1. 形式の概要と歴史的背景

「IYUV」は FourCC(Four Character Code)や一部の古いライブラリで使われる識別子です。実際のピクセルメモリ上の並びは、Y(輝度)平面を先頭に置き、その後に U(Cb)平面、最後に V(Cr)平面を続ける「Y-U-V」の順で並ぶ点が特徴です。一般に I420、IYUV、YU12 は同じ配置(Y、U、V)を指すことが多く、YV12 は U と V の順序が逆(Y、V、U)となります。

2. メモリ配置(レイアウト)とサイズ計算

  • 色深度:通常 8bit/成分(1バイト/成分)。
  • サンプリング:4:2:0(横方向 2 倍、縦方向 2 倍でクロマを間引く)。
  • 平面構造:Y 平面(W×H)、U 平面(W/2×H/2)、V 平面(W/2×H/2)。
  • 総バイト数(8bit の場合):W × H × 1.5(すなわち Y が WH、U と V がそれぞれ WH/4)。

例:幅 640、高さ 480 の場合
Y = 640×480 = 307200 バイト、U = V = 320×240 = 76800 バイト。合計 = 307200 + 76800 + 76800 = 460800 バイト(= 640×480×1.5)。

注意点:奇数幅・奇数高さの処理は実装依存です。多くのコーデックやハードウェアは偶数倍(特に 2 の倍数)を前提にしているため、入力画像の幅・高さは偶数に揃える(パディング)ことが多いです。クロマの幅/高さ計算では ceil((W)/2) や floor などの実装差が出ますので、互換性を要する場合は仕様を確認してください。

3. クロマサンプリング(4:2:0)の意味とクロマ配置の差

4:2:0 は 2x2 のピクセルブロックごとに 1 つの U と 1 つの V を持つことを意味しますが、「クロマの位置(chroma siting)」には複数の慣習があります。すなわち、クロマサンプルが 2x2 ブロックのどの位置に対応するか(ブロック中央かピクセル格子の隅かなど)は標準やエンコーダ/デコーダによって異なります。これを無視すると色ずれ(特に細かい色境界でのずれ)が生じます。

主要な規格例:

  • ITU(ビデオ)系:クロマのサンプリング位置指定が存在する(MPEG 系 / ITU-T などで差あり)。
  • JPEG 系や一部のイメージ処理系:別の配置を取ることがある。

実務では、映像を送受信する際に「どのクロマサイティングを使うか(またはどのライブラリが期待しているか)」を合わせることが重要です。

4. 色空間(YUV と Y'CbCr、レンジ)の注意

しばしば「YUV」と呼ばれますが、デジタル映像では正確には Y'CbCr(ガンマ補正済みの輝度・色差)と呼ぶ方が正確です。さらに色空間変換や RGB への変換では、使用する標準(BT.601、BT.709、BT.2020 等)により係数やスケーリングが変わります。

レンジにも注意が必要です。テレビ規格に基づく「ビデオレンジ(リミテッドレンジ)」では Y が 16–235、Cb/Cr が 16–240 の範囲で表現されることが多く、一方でフルレンジ(0–255)を使うケースもあります。レンジの取り扱いを誤るとコントラストや色偏差が発生します。

5. IYUV / I420 と YV12 / NV12 などの違い

  • I420 / IYUV / YU12:Y, U, V の順で平面が並ぶ(Y → U → V)。FFmpeg では "yuv420p"(AV_PIX_FMT_YUV420P)に相当することが多い。
  • YV12:Y, V, U の順(Y → V → U)。古い Windows API や一部のコードで使われる。
  • NV12:半平面(semi-planar)で、Y 平面の後に UV が交互にインターリーブされる(U0 V0 U1 V1 ...)。GPU/ハードウェアや一部のハードデコーダで使われることが多い。

つまり同じ「YUV420」でもメモリ上の並びが違うため、バッファを直接 reinterpret すると色が崩れます。FourCC やピクセルフォーマット名で正確に指定することが不可欠です。

6. RGB との変換(数式と実用上の実装)

8bit(フルレンジ)の簡易変換(代表的な式):

  • R = Y + 1.402 × (Cr - 128)
  • G = Y - 0.344136 × (Cb - 128) - 0.714136 × (Cr - 128)
  • B = Y + 1.772 × (Cb - 128)

ビデオレンジ(リミテッドレンジ)を使う場合は、Y のオフセット(16)やスケーリング係数(1.164 ≒ 255/(235-16))などを適用する必要があります。さらに BT.601 と BT.709 で係数が変わるため、正しい色再現には色空間情報が必要です。

7. 実践的な利用シーンとツール

  • FFmpeg:raw YUV を扱う場合は -pix_fmt yuv420p(I420 相当)で指定可能。raw ファイル表示には ffplay -f rawvideo -pix_fmt yuv420p -video_size WxH file.yuv。
  • OpenCV:cvtColor による COLOR_YUV2BGR_I420 / COLOR_YUV2RGB_I420 などが用意されている。
  • libyuv(Google/Chromium):高速な平面間コピー・変換(I420 ↔ RGBA、回転、スケールなど)に広く使われる。
  • Android:ImageFormat.YUV_420_888 は抽象化された 4:2:0 の表現で、内部的に I420(またはその他)へマップされることがある。カメラ画像処理ではよく登場する。
  • WebRTC:内部で I420 を扱う実装が多く、リアルタイム映像処理のデファクト標準的な扱い方になっているケースがある。

8. パフォーマンスとハードウェア考慮

平面形式(I420)はメモリアクセスが予測可能で SIMD 最適化やゼロコピーがしやすく、高速化に有利です。一方で GPU にテクスチャとしてアップロードする際には NV12 の方がサポートが豊富で効率的な場合が多い(半平面でサンプラが少なく済むため)。

また、行ストライド(stride / pitch)やアラインメント(16 バイトなど)の扱いも重要です。多くのデコーダ/エンコーダは行のバイト数を 16 / 32 の倍数にアラインして返すため、単純な W×H×1.5 計算だけでバッファ操作を行うと不具合が出ます。必ず stride を API から取得して扱うこと。

9. よくある落とし穴(チェックリスト)

  • Y/U/V の順序(I420 と YV12 を取り違えない)。
  • 奇数幅・高さの取り扱い(多くの実装が偶数前提)。
  • クロマサイティングの違いで生じる色ズレ。
  • フルレンジとリミテッドレンジの混同によるコントラスト変化。
  • 行ストライド(stride)を無視したメモリコピー。

10. 実例コマンドと簡単なコード例(参考)

FFmpeg で任意の動画を生の I420 (.yuv)に変換する例:

ffmpeg -i input.mp4 -pix_fmt yuv420p -an -sn output.yuv

raw YUV を再生(幅 640 高さ 480 の場合):

ffplay -f rawvideo -pixel_format yuv420p -video_size 640x480 output.yuv

まとめ

IYUV(I420)はシンプルで扱いやすい YUV 4:2:0 平面フォーマットとして、映像処理やリアルタイム通信、コーデック内部で広く使われます。しかし、フォーマット名の揺れ(I420 / IYUV / YU12 / YV12 / NV12 等)、クロマ配置、色空間やレンジ、stride の扱いなど、実装の互換性に関わる注意点が多数あります。実務では「どの仕様を前提としているか」を明確にし、API のドキュメントやツール(FFmpeg, libyuv, OpenCV, Android)に従って扱うことが重要です。

参考文献