YUYVとRGB3の相互変換

RGB3とは

RGB3とは動画フォーマットの一つで、1ピクセルにつきR, G, B の3つの8ビットの値を用いるものです。大きさLの画像を表すために3Lの配列を用いて、3*iにiピクセル目のRの値が、3*i+1にiピクセル目のGの値が、3*i+2にiピクセル目のBの値が入っています。

YUYVとは

YUYVも動画フォーマットの一つで、1ピクセルにつきY, U, V の3つの8ビットの値を用いるYUVというフォーマットのシリーズの一つです。RGBと同じように大きさLの画像を表すために3Lの大きさの配列が必要なようにも思えますが、YUVのうちUやVは適当に間引いても人間の目にはあまり変化が無いらしく、YUYVではLの大きさの画像を表すために長さ2Lの配列を使います。

YUYVの名前から想像できる通り、2*iにiピクセル目のYの値、2*i+1にiピクセル目のUの値、2*(i+1)にi+1ピクセル目のYの値、2*(i+1)+1にi+1ピクセル目のVの値を入れます。このように、UとVの値は2ピクセルにつき1つしか保存しません。

YUYV=>RGB3変換

R, G, B の値は Y, U, V の値から計算できます。UVの値は2ピクセルにつき1つずつしか無いので再利用します。

fn yuyv2rgb(yuyv: &[u8]) -> Vec<u8> {
    assert_eq!(yuyv.len() % 2, 0);
    let length = yuyv.len() / 2;
    let mut rgb = vec![0; length * 3];
    for i in 0..length {
        let pos = i / 2 * 4;

        let y = if i % 2 == 0 {
            (yuyv[pos] as i64) << 8
        } else {
            (yuyv[pos + 2] as i64) << 8
        };
        let u = (yuyv[pos + 1] as i64) - 128;
        let v = (yuyv[pos + 3] as i64) - 128;

        let r = (y + (359 * v)) >> 8;
        let g = (y - (88 * u) - (183 * v)) >> 8;
        let b = (y + (454 * u)) >> 8;
        rgb[3 * i + 0] = r.min(255).max(0) as u8;
        rgb[3 * i + 1] = g.min(255).max(0) as u8;
        rgb[3 * i + 2] = b.min(255).max(0) as u8;
    }
    assert_eq!(rgb.len(), 3 * length);
    rgb
}

RGB3=>YUYV変換

RGBの値からYUVの値を計算できますが、UVはそれぞれ2回に1回しか保存しません。

fn rgb2yuyv(rgb: &[u8]) -> Vec<u8> {
    assert_eq!(rgb.len() % 3, 0);
    let length = rgb.len() / 3;
    let mut yuyv = vec![0; length * 2];
    for i in 0..length {
        let r = rgb[3 * i + 0] as f64;
        let g = rgb[3 * i + 1] as f64;
        let b = rgb[3 * i + 2] as f64;
        let y = 0.257 * r + 0.504 * g + 0.098 * b + 16.0;
        let u = -0.148 * r - 0.291 * g + 0.439 * b + 128.0;
        let v = 0.439 * r - 0.368 * g - 0.071 * b + 128.0;
        if i % 2 == 0 {
            yuyv[2 * i + 0] = y.max(0.0).min(255.0) as u8;
            yuyv[2 * i + 1] = u.max(0.0).min(255.0) as u8;
        } else {
            yuyv[2 * i + 0] = y.max(0.0).min(255.0) as u8;
            yuyv[2 * i + 1] = v.max(0.0).min(255.0) as u8;
        }
    }
    yuyv
}