2012年1月7日土曜日

ConvertToRGB

avcodecに10bit-AVCデコーダがcommitされて随分経つのにいまだによく解らない人のための乱暴な解説。

まずはこちらの方の解説を読みましょう。
自分が知るかぎりでは、もっともシンプルでわかりやすい説明をされています。
DTVかくし味 - YUVとRGBの比較

特にこの図、
いかに暗部の色数が少ないか、なぜ暗部にシルバーグレインを撒く(=Yの値だけまばら且つ適当に上げる)とマシに見えるようになるかが一発で理解できます。

さて、「YUVはRGBで表現できる範囲をすべてカバーしています。(が、細かい諧調はRGBに分があります。)」とまとめられていますが、これをちょっと具体的に計算してみましょうか。
/* yuv2rgb.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SATURATE(X) X = (X < 0) ? 0 : (X > 255) ? 255 : X

int main(void)
{
    const struct {
        char *name;
        int coef[6];
    } matrix[] = {
        {"rec601", {76309, 104597, 25675, 53279, 76308, 132201}},
        {"pc601",  {65536,  91881, 22553, 46801, 65536, 116129}},
        {"rec709", {76309, 117504, 13954, 34903, 76308, 138453}},
        {"pc709",  {65536, 103219, 12257, 30659, 65536, 121621}},
        {NULL}
    };

    int count = 256 * 256 * 256;
    int *rgb = (int*)calloc(count, sizeof(int));
    if (!rgb) {
        fprintf(stderr, "malloc failed\n");
        return -1;
    }
    for (int m = 0; matrix[m].name; m++) {
        int coef0 = matrix[m].coef[0], coef1 = matrix[m].coef[1],
            coef2 = matrix[m].coef[2], coef3 = matrix[m].coef[3],
            coef4 = matrix[m].coef[4], coef5 = matrix[m].coef[5];
        int x = !strncmp(matrix[m].name, "rec", 3) ? 16 : 0;

        for (int y = 0; y < 256; y++) {
            for (int u = 0; u < 256; u++) {
                for (int v = 0; v < 256; v++) {
                    /* 下記計算式は茂木和博氏のm2v.vfpのreadmeより引用しました */
                    int r = (coef0 * (y - x) + coef1 * (v - 128)) >> 16;
                    int g = (coef0 * (y - x) - coef2 * (u - 128) - coef3 * (v - 128)) >> 16;
                    int b = (coef4 * (y - x) + coef5 * (u - 128)) >> 16;
                    SATURATE(r);
                    SATURATE(g);
                    SATURATE(b);
                    rgb[((r << 16) | (g << 8) | b)]++;
                }
            }
        }

        int num = 0;
        for (int i = 0; i < count; i++) {
            num += !!rgb[i];
            rgb[i] = 0;
        }
        printf("%s: %d\n", matrix[m].name, num);
    }
    free(rgb);
    return 0;
}
このコードでBT.601とBT.709で8bitYUV->8bitRGBに変換した際、RGBで使われる色数がわかります。 で、結果は
BT.601 TVレンジ(伸張)      2,956,082色
BT.601 PCレンジ(ストレート) 4,261,687色
BT.709 TVレンジ(伸張)      3,048,157色
BT.709 PCレンジ(ストレート) 4,400,772色
となります。
いやぁ、ほんと色数少ないですね。TrueColorRGBの16,777,216色のうち、最大でも4分の1程度しか使われていません。
バンディングが発生するのも当たり前です。
これが10bitYUV->8bitRGBだとどの程度になるのかは、適当な計算式がとりあえず見当たらなかったのでやってませんが、8bit->10bitでY,U,Vの精度がそれぞれ4倍になることを考えれば、色数もかなり良い感じに増えそうです。

てなわけで、10bitエンコードの力を確認するためにバカ高い業務用モニタ等は別に必要ありません。
民生品でもそこそこの品質があれば違いは確実にわかります(もちろん、良いものであればもっとよく分かるでしょうが)。

つーか、AviUtlのプレビューがやたら綺麗に見える理由って、まさにこれだよね。

0 件のコメント:

コメントを投稿