Skip to content

Commit

Permalink
yuv: Add YUV420p (planar) support
Browse files Browse the repository at this point in the history
Signed-off-by: Christopher N. Hesse <raymanfx@gmail.com>
  • Loading branch information
raymanfx committed Feb 6, 2023
1 parent d90b238 commit e133a7d
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 2 deletions.
25 changes: 23 additions & 2 deletions ffimage-yuv/benches/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use ffimage::{
color::Rgb,
iter::{ColorConvertExt, PixelsExt, WriteExt},
};
use ffimage_yuv::{yuv::Yuv, yuv422::Yuv422};
use ffimage_yuv::{yuv::Yuv, yuv420::Yuv420p, yuv422::Yuv422};

pub fn yuv_to_rgb(c: &mut Criterion) {
let resolutions = [(640, 480), (1280, 720)];
Expand Down Expand Up @@ -51,4 +51,25 @@ pub fn yuv422_to_rgb(c: &mut Criterion) {
}
}

criterion_group!(benches, yuv_to_rgb, yuv422_to_rgb);
pub fn yuv420p_to_rgb(c: &mut Criterion) {
let resolutions = [(640, 480), (1280, 720)];

for res in resolutions {
let yuv420p = vec![10; ((res.0 * res.1) as f32 * 1.5) as usize];
let mut rgb = vec![10; res.0 * res.1 * 3];

c.bench_function(
&format!("Yuv420p[u8] -> Rgb[u8] ({}x{})", res.0, res.1),
|b| {
b.iter(|| {
Yuv420p::pack(&yuv420p)
.into_iter()
.colorconvert::<Rgb<u8>>()
.write(black_box(&mut rgb));
})
},
);
}
}

criterion_group!(benches, yuv_to_rgb, yuv422_to_rgb, yuv420p_to_rgb);
1 change: 1 addition & 0 deletions ffimage-yuv/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![no_std]

pub mod yuv;
pub mod yuv420;
pub mod yuv422;
57 changes: 57 additions & 0 deletions ffimage-yuv/src/yuv420.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use crate::yuv::Yuv;

/// YUV 4:2:0 (Planar)
///
/// This is a zero-sized struct, providing useful functions for handling planar YUV images.
/// The planar format is often used for video encoding / decoding usecases. Most cameras which
/// output YUV image frames will usually use a packet format, e.g. YUYV aka Y422.
pub struct Yuv420p;

impl Yuv420p {
pub fn pack<'a, T>(buf: &'a [T]) -> impl IntoIterator<Item = Yuv<T>> + 'a
where
T: Copy,
{
// buf must have a rectangular shape
assert_eq!(buf.len() % 2, 0);
// buf must be divisible in 3 parts: 2/3 Luma (y) samples, 1/3 Chroma (u + v) samples
assert_eq!(buf.len() % 3, 0);

let pixels = (buf.len() / 3) * 2;
let width = pixels / 4;

let y = &buf[0..pixels];
let u = &buf[pixels..(pixels + width)];
let v = &buf[(pixels + width)..(pixels + width * 2)];

Yuv420p::pack_planes(y, u, v)
}

pub fn pack_planes<'a, T>(
y: &'a [T],
u: &'a [T],
v: &'a [T],
) -> impl IntoIterator<Item = Yuv<T>> + 'a
where
T: Copy,
{
// YUV420 has 2x2 blocks of Luma (y) samples
assert_eq!(y.len() % 4, 0);
let width = y.len() / 4;
let height = y.len() / width;
assert_eq!(u.len(), width);
assert_eq!(v.len(), width);

(0..height)
.into_iter()
.zip((0..width).into_iter())
.map(move |(i, j)| {
let y_idx = i * width + j;
let uv_idx = i / 2 * width / 2 + j / 2;
let y = y[y_idx];
let u = u[uv_idx];
let v = v[uv_idx];
Yuv([y, u, v])
})
}
}

0 comments on commit e133a7d

Please sign in to comment.