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 }