RGBとYCrCbの変換(明度と色差)

 ピクセルの色を表す方法には、赤緑青という3原色で表すRGBの他に「色」と「明るさ」に分ける方法もあります。色と明るさを分ける事で明るさと色を独立に扱う事ができるため、明るさだけを増減したり明るさを変えないで色のバランスを変える、といった処理も可能になります。今回は、24ビットフルカラーDIBRGB成分を明度YCr,Cbという二つの「色差」に分けるYCrCbに変換してみる事にしました。

RGBとYCrCbの対応

 ビットマップをYCrCbに変換するには、RGBで表されているDIBの各ピクセルに対して「明るさ」と「色差」を計算する必要があります。その式は、以下のとおり(以前の式はCrCbが逆になっていたようです。村橋さん、ご指摘ありがとうございました)。

Y= 0.299R+0.587G+0.114B
Cr= 0.500R-0.419G-0.081B
Cb= -0.169R-0.332G+0.500B

 このうちYは、グレースケールで表す時の明度ですね。このYでピクセルの「明るさ」を、そしてCrCbで色を表す、と。逆に、YCrCbからRGBを求めるには、上の式を行列で表して係数の逆行列を求めれば良いので以下のようになります。

R= Y+1.402Cr
G= Y-0.714Cr-0.344Cb
B= Y+1.772Cb

YCrCb変換処理

 では、実際に各ピクセルのRGBYCrCbに変換してみましょう。今回は、YCrCbそれぞれに1バイトずつ割り当てる事にします。上の式を見るとCrCbは0を中心にプラス・マイナス両方向に広がりそうですね。これを0〜255に対応させるため、計算結果に128を足して0〜255の範囲に収めることにしました。

  y=0.299*r+0.587*g+0.114*b;
  cr=0.5*r-0.419*g-0.081*b+128;
  cb=-0.169*r-0.332*g+0.5*b+128;

  if (y<0)
      y=0;
  if (y>255)
      y=255;

  if (cr<0)
      cr=0;
  if (cr>255)
      cr=255;

  if (cb<0)
      cb=0;
  if (cb>255)
      cb=255;

 プログラムでは、この処理をビットマップの各ピクセルに対して行います。今回は、元のDIBを24ビットフルカラーBMPから作成するので、YCrCbへの変換結果もビット数では同じです(ただし、誤差が入るため画質は劣化するはず)。プログラムでは、ビットマップを読みこんだらそのビットマップを変換してカレントディレクトリのtest.yuvというファイルに保存する事にしました。ファイルの構造は、先頭8バイトに縦横のサイズ(それぞれ4バイト)が入りその後に変換した結果のYCrCbビット列をそのまま書き出します。

yuvファイルの読み込み

 では、次にこうして作ったyuvファイルを読みこんでみましょう。まず先頭2ダブルワードでビットマップの大きさを取得したらその大きさの24ビットフルカラーDIBを作り、yuvファイルのピクセル列からDIBのピクセルを作成します。変換は単に式に当てはめるだけなので、特に難しい点はないでしょう。

 読み込んだファイルの先頭をlpWork、サイズがdwWidth×dwHeightDIBのピクセル列の先頭をlpPixelとすれば以下のようになりますね。

  for (i=0;i<dwHeight;i++)
      for (j=0;j<dwWidth;j++) {

          y=lpWork[j*3+i*dwWidth*3+8];
          cr=lpWork[j*3+i*dwWidth*3+9]-128;
          cb=lpWork[j*3+i*dwWidth*3+10]-128;

          r=y+1.402*cr;
          g=y-0.714*cr-0.344*cb;
          b=y+1.772*cb;

          if (r<0)
              r=0
          if (r>255)
              r=255;

          if (g<0)
              g=0
          if (g>255)
              g=255;

          if (b<0)
              b=0
          if (b>255)
              b=255;

          lpPixel[j*3+i*dwLength]=(BYTE)b;
          lpPixel[j*3+i*dwLength+1]=(BYTE)g;
          lpPixel[j*3+i*dwLength+2]=(BYTE)r;

      }

 変換の時にCrCbに128を足したので、RGBへの変換処理の前に128を引いています。

プログラム

 実行したら、ウインドウに適当なビットマップファイルをドロップしてください。ビットマップの各ピクセルがRGBからYCrCbに変換され、test.yuvというファイルが作成されます。このファイルをウインドウにドロップするとRGBに復元されて表示されるので、続いて元のビットマップをドロップして画像を比べてみましょう。

プログラムソース表示


画像処理実験室 > プログラミング資料庫