読者です 読者をやめる 読者になる 読者になる

酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

YCbCr422のフォーマットについて考えてみよう/RGB888→YCbCr422変換を実装する

「YCbCr422 フォーマット」をキーワードにGoogleで検索してくる方が多いので若き技術者(←妄想)の為に解説します。

偏った知識のみで書きますので間違っていたら指摘してください、お願いします。

カメラセンサーからRaw画像が出力されます。センサーから出力された生(Raw)の状態でJpegなどにエンコードされる前の状態です。

f:id:ch3cooh393:20160801175753p:plain

私が扱っていたカメラセンサーからは、Raw画像がYCbCr422フォーマットで出力されるされることが多かったです。

まずYCbCr422について書く前に、YCbCrやYUVはセンサーのメーカーや種類によって、それぞれ独自のフォーマット(特にメモリ上のCb成分やCr成分の配置場所)によって表現されますので、ここで扱うのは私が知ってるものになります。

Y、Cb、Crの意味

YCbCr422は隣り合う2ピクセルを1組として扱います。よって基本的に横幅が奇数ピクセルの画像は存在しません。(下図は一例です)

f:id:ch3cooh393:20160219124350p:plain

YCbCr422は、Y1、Y2、Cb、Crの4つの成分で表現します。それぞれの成分は1バイトで表現します。ちなみに

  • Y1は、1ピクセル目の輝度成分
  • Y2は、2ピクセル目の輝度成分
  • Cbは、Y1とY2の青色色差成分
  • Crは、Y1とY2の赤色色差成分

を意味しています。

YCbCr422のフォーマット

メーカー毎の独自フォーマットと言っていたのは、このブロックの配置の違いからきます。

  • 2ピクセルを一緒くたにして扱う方法。(後ほど記述するコードはこのフォーマットを採用しています)
Cb1, Y1, Cr1, Y2, Cb2, Y3, Cr2, Y4 ...
  • Y、Cb、Crで成分毎に分ける方法。
Y1, Y2, Y3, Y4 ...
Cb1, Cb2, Cb3, Cb4 ... 
Cr1, Cr2, Cr3, Cr4 ... 
  • 輝度ブロック、色差成分ブロックで分ける方法。
Y1, Y2, Y3, Y4 ...
Cb1, Cr1, Cb2, Cr2, Cb3, Cr3 ...

他にもあるかもしれません。

RGB888→YCbCr422変換をコーディングしてみた。

何故必要になったかを覚えていないのですが、おそらくWindows Bitmap画像からテスト用のYCbCr422画像を生成する必要がでてきたからだと思います。

/*!
    RGB888をYCbCr422に変換すると思う
    @param[out] dst 加工後画像
    @param[in] src 元画像
    @param[in] width 画像横サイズ
    @param[in] height 画像縦サイズ
    @retval true  成功    
            false 失敗
    @note   ITU-R BT.601に基づいた計算式を使用しております
            Y =   0.257R + 0.504G + 0.098B + 16  
            Cb = -0.148R - 0.291G + 0.439B + 128 
            Cr =  0.439R - 0.368G - 0.071B + 128 
*/
bool rgb888_to_ycbcr422
(
 unsigned int *dst, 
 const unsigned char *src, 
 int width, 
 int height
)
{
    unsigned char y1,y2,cb,cr;
    unsigned char r1,g1,b1;
    unsigned char r2,g2,b2;

    //横サイズチェック
    if(width%2) return false;

    //2ピクセル同時に処理する
    for(int cnt=0; cnt<(width*height/2); cnt++)
    {
        //色成分を取得
        r1 = *(src + (cnt * 6));
        g1 = *(src + (cnt * 6) + 1);
        b1 = *(src + (cnt * 6) + 2);
        r2 = *(src + (cnt * 6) + 3);
        g2 = *(src + (cnt * 6) + 4);
        b2 = *(src + (cnt * 6) + 5);

	///誤差が出るのでこのコードを
        ///使うときはうまく丸めてください

        //輝度成分の計算を行う
        y1 = ((257 * r1) + (504 * g1) + (98 * b1)) / 1000 + 16;
        y2 = ((257 * r2) + (504 * g2) + (98 * b2)) / 1000 + 16;

        //色差成分の計算を行う
        cb = (((439 * b1) - (148 * r1) - (291 * g1))
            + ((439 * b2) - (148 * r2) - (291 * g2))) / 2 / 1000 + 128;
        cr = (((439 * r1) - (368 * g1) - ( 71 * b1))
            + ((439 * r2) - (368 * g2) - ( 71 * b2))) / 2 / 1000 + 128;

        //計算結果を Cb Y1 Cr Y2 の順番に格納する
        *(dst + cnt) = (y2<<24 | cr<<16 | y1<<8 | cb);
    }

    return true;
}

YUV Viewというツールで色味を見ていたんだけど、取り扱いがフルスケールYUVでした。。。色味見てもそりゃ違うわな・・・

ちなみに、YCbCr-RGBの変換について詳しく書かれているこちらのサイトを参考にさせて頂きました。

関連記事

blog.ch3cooh.jp