mjpeg, yuv test

This commit is contained in:
l1npengtul
2025-11-11 05:16:33 +09:00
parent 68716fc3d5
commit 3b0ae537ba
6 changed files with 1119 additions and 534 deletions
+39 -21
View File
@@ -1,11 +1,11 @@
use crate::error::NokhwaError;
use crate::frame_buffer::FrameBuffer;
use crate::image::{DecodedImage, NonFloatScalarWidth};
use crate::types::{Resolution};
use crate::pixel_destination::PixelDestination;
use crate::types::Resolution;
use bytemuck::try_cast_slice_mut;
pub use image::{ImageBuffer, Pixel, Primitive};
use std::fmt::Debug;
use bytemuck::try_cast_slice_mut;
use crate::pixel_destination::PixelDestination;
pub trait Decoder {
type Config: Clone + Debug + ConfigHasResolution;
@@ -23,14 +23,25 @@ pub trait Decoder {
destination_format: PixelDestination,
) -> Result<Self::OutputMeta, NokhwaError>;
fn decode_to_pixel_buffer<P: Pixel>(&mut self, to_decode: FrameBuffer, mut buffer: impl AsMut<[P::Subpixel]>) -> Result<Self::OutputMeta, NokhwaError>
fn decode_to_pixel_buffer<P: Pixel>(
&mut self,
to_decode: FrameBuffer,
mut buffer: impl AsMut<[P::Subpixel]>,
) -> Result<Self::OutputMeta, NokhwaError>
where
<P as Pixel>::Subpixel: NonFloatScalarWidth
<P as Pixel>::Subpixel: NonFloatScalarWidth,
{
let Some(destination) = PixelDestination::get_by_pixel::<P>() else { return Err(NokhwaError::DecoderUnknownDestinationPixelFormat(P::COLOR_MODEL, P::Subpixel::WIDTH_BYTES)) };
let Some(destination) = PixelDestination::get_by_pixel::<P>() else {
return Err(NokhwaError::DecoderUnknownDestinationPixelFormat(
P::COLOR_MODEL,
P::Subpixel::WIDTH_BYTES,
));
};
if !Self::supports_destination(destination) {
return Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat(destination))
return Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat(
destination,
));
}
let buffer = buffer.as_mut();
@@ -52,28 +63,35 @@ pub trait Decoder {
let mut out_buffer: Vec<P::Subpixel> = vec![P::Subpixel::DEFAULT_MIN_VALUE; min_size_alloc];
let meta = self.decode_to_pixel_buffer::<P>(to_decode, &mut out_buffer)?;
Ok(DecodedImage::new(
ImageBuffer::from_vec(
resolution.width(),
resolution.height(),
out_buffer,
)
.ok_or(NokhwaError::Decoder(
"failed to convert into an image buffer".to_string(),
))?,
ImageBuffer::from_vec(resolution.width(), resolution.height(), out_buffer).ok_or(
NokhwaError::Decoder("failed to convert into an image buffer".to_string()),
)?,
meta,
))
}
fn output_decoder_min_size_pixel<P>(&self, resolution: Resolution) -> Result<usize, NokhwaError> where
fn output_decoder_min_size_pixel<P>(&self, resolution: Resolution) -> Result<usize, NokhwaError>
where
P: Pixel,
<P as Pixel>::Subpixel: NonFloatScalarWidth {
PixelDestination::get_by_pixel::<P>().map(|dest| self.output_decoder_min_size(resolution, dest)).ok_or(NokhwaError::DecoderUnknownDestinationPixelFormat(P::COLOR_MODEL, P::Subpixel::WIDTH_BYTES))?
<P as Pixel>::Subpixel: NonFloatScalarWidth,
{
PixelDestination::get_by_pixel::<P>()
.map(|dest| self.output_decoder_min_size(resolution, dest))
.ok_or(NokhwaError::DecoderUnknownDestinationPixelFormat(
P::COLOR_MODEL,
P::Subpixel::WIDTH_BYTES,
))?
}
fn output_decoder_min_size(&self, resolution: Resolution, destination_format: PixelDestination) -> Result<usize, NokhwaError> {
fn output_decoder_min_size(
&self,
resolution: Resolution,
destination_format: PixelDestination,
) -> Result<usize, NokhwaError> {
if !Self::supports_destination(destination_format) {
return Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat(destination_format))
return Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat(
destination_format,
));
}
let px_size = match destination_format {
+21
View File
@@ -146,3 +146,24 @@ impl Deref for FrameBuffer<'_> {
self.buffer.as_ref()
}
}
impl From<Vec<u8>> for FrameBuffer<'_> {
fn from(value: Vec<u8>) -> Self {
let buffer = Cow::Owned(value);
FrameBuffer {
buffer,
metadata: None,
}
}
}
impl<'a> From<&'a [u8]> for FrameBuffer<'a> {
fn from(value: &'a [u8]) -> Self {
let buffer = Cow::Borrowed(value);
FrameBuffer {
buffer,
metadata: None,
}
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ ffmpeg = ["ffmpeg-the-third"]
yuyv = [ "dcv-color-primitives", "yuv" ]
mjpeg = [ "zune-jpeg", "zune-core" ]
luma = [ "nokhwa-iter-extensions", "itermore" ]
static = ["ffmpeg-the-third/static"]
# static = ["ffmpeg-the-third/static"]
#async = []
[dependencies]
+246 -166
View File
@@ -1,16 +1,16 @@
use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt::Debug;
use bytemuck::{cast_slice, cast_slice_mut};
use itermore::IterArrayChunks;
use nokhwa_core::decoder::{ConfigHasResolution, Decoder};
use nokhwa_core::error::NokhwaError;
use nokhwa_core::frame_buffer::FrameBuffer;
use nokhwa_core::frame_format::{CustomFrameFormat, FrameFormat};
use nokhwa_iter_extensions::duplicate::IterDuplicateConst;
use nokhwa_iter_extensions::interweave::IterInterweave;
use itermore::{IterArrayChunks};
use nokhwa_core::pixel_destination::PixelDestination;
use nokhwa_core::types::{CameraFormat, Resolution};
use nokhwa_iter_extensions::duplicate::IterDuplicateConst;
use nokhwa_iter_extensions::interweave::IterInterweave;
use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt::Debug;
#[derive(Clone, Debug, PartialEq)]
pub struct LumaDecoder {
@@ -40,15 +40,21 @@ impl Decoder for LumaDecoder {
Ok(())
}
fn decode_to_buffer(&mut self, to_decode: FrameBuffer, mut buffer: impl AsMut<[u8]>, destination_format: PixelDestination) -> Result<Self::OutputMeta, NokhwaError> {
let format = self.config().custom_frame_format_map.as_ref().and_then(|m| {
match self.config().format {
FrameFormat::Custom(cfmt) => {
m.get(&cfmt).copied()
}
fn decode_to_buffer(
&mut self,
to_decode: FrameBuffer,
mut buffer: impl AsMut<[u8]>,
destination_format: PixelDestination,
) -> Result<Self::OutputMeta, NokhwaError> {
let format = self
.config()
.custom_frame_format_map
.as_ref()
.and_then(|m| match self.config().format {
FrameFormat::Custom(cfmt) => m.get(&cfmt).copied(),
_ => None,
}
}).unwrap_or(self.config().format);
})
.unwrap_or(self.config().format);
let r = filter_to_u8(self.config().channel_filters.red);
let g = filter_to_u8(self.config().channel_filters.green);
@@ -63,11 +69,12 @@ impl Decoder for LumaDecoder {
let buffer = buffer.as_mut();
match format {
FrameFormat::Luma_8 => {
match destination_format {
FrameFormat::Luma_8 => match destination_format {
PixelDestination::Luma8 => {
if to_decode.len() != buffer.len() {
return Err(NokhwaError::DecoderInvalidBuffer("Lengths differ!".to_string()))
return Err(NokhwaError::DecoderInvalidBuffer(
"Lengths differ!".to_string(),
));
}
match to_decode.consume().0 {
@@ -85,113 +92,138 @@ impl Decoder for LumaDecoder {
let default_alpha = u8::MAX * a;
if (to_decode.len() * 2) != buffer.len() {
return Err(NokhwaError::DecoderInvalidBuffer("Lengths differ!".to_string()))
return Err(NokhwaError::DecoderInvalidBuffer(
"Lengths differ!".to_string(),
));
}
to_decode.buffer().iter().interweave::<0>(&default_alpha, false).enumerate()
.for_each(|(len, data)| {
unsafe {
to_decode
.buffer()
.iter()
.interweave::<0>(&default_alpha, false)
.enumerate()
.for_each(|(len, data)| unsafe {
*buffer.get_unchecked_mut(len) = *data;
}
});
Ok(())
}
PixelDestination::Rgb8 => {
if (to_decode.len() * 3) != buffer.len() {
return Err(NokhwaError::DecoderInvalidBuffer("Lengths differ!".to_string()))
return Err(NokhwaError::DecoderInvalidBuffer(
"Lengths differ!".to_string(),
));
}
to_decode.buffer().iter().duplicate_const::<3>().arrays::<3>().flat_map(|pixel| {
to_decode
.buffer()
.iter()
.duplicate_const::<3>()
.arrays::<3>()
.flat_map(|pixel| {
let px_r = *pixel[0_usize] * r;
let px_g = *pixel[1_usize] * g;
let px_b = *pixel[2_usize] * b;
[px_r, px_g, px_b]
}).enumerate().for_each(|(len, data)| {
unsafe {
})
.enumerate()
.for_each(|(len, data)| unsafe {
*buffer.get_unchecked_mut(len) = data;
}
});
Ok(())
}
PixelDestination::Rgba8 => {
if (to_decode.len() * 4) != buffer.len() {
return Err(NokhwaError::DecoderInvalidBuffer("Lengths differ!".to_string()))
return Err(NokhwaError::DecoderInvalidBuffer(
"Lengths differ!".to_string(),
));
}
to_decode.buffer().iter().duplicate_const::<3>().arrays::<3>().flat_map(|pixel| {
to_decode
.buffer()
.iter()
.duplicate_const::<3>()
.arrays::<3>()
.flat_map(|pixel| {
let px_r = *pixel[0_usize] * r;
let px_g = *pixel[1_usize] * g;
let px_b = *pixel[2_usize] * b;
let px_a = 255 * b;
[px_r, px_g, px_b, px_a]
}).enumerate().for_each(|(len, data)| {
unsafe {
})
.enumerate()
.for_each(|(len, data)| unsafe {
*buffer.get_unchecked_mut(len) = data;
}
});
Ok(())
}
PixelDestination::Rgb16 => {
if (to_decode.len() * 6) != buffer.len() {
return Err(NokhwaError::DecoderInvalidBuffer("Lengths differ!".to_string()))
return Err(NokhwaError::DecoderInvalidBuffer(
"Lengths differ!".to_string(),
));
}
let temp_buffer = cast_slice_mut::<u8, u16>(buffer);
let factor = match self.config().mode {
ConvertMode::Scaled => {
u16::MAX / (u8::MAX as u16)
}
ConvertMode::Clipped => {
1_u16
}
ConvertMode::Scaled => u16::MAX / (u8::MAX as u16),
ConvertMode::Clipped => 1_u16,
};
to_decode.buffer().iter().duplicate_const::<3>().arrays::<3>().flat_map(|pixel| {
to_decode
.buffer()
.iter()
.duplicate_const::<3>()
.arrays::<3>()
.flat_map(|pixel| {
let px_r = (*pixel[0_usize] as u16) * r_u16 * factor;
let px_g = (*pixel[1_usize] as u16) * g_u16 * factor;
let px_b = (*pixel[2_usize] as u16) * b_u16 * factor;
[px_r, px_g, px_b]
}).enumerate().for_each(|(len, data)| {
unsafe {
})
.enumerate()
.for_each(|(len, data)| unsafe {
*temp_buffer.get_unchecked_mut(len) = data;
}
}); Ok(())
});
Ok(())
}
PixelDestination::Rgba16 => {
if (to_decode.len() * 8) != buffer.len() {
return Err(NokhwaError::DecoderInvalidBuffer("Lengths differ!".to_string()))
return Err(NokhwaError::DecoderInvalidBuffer(
"Lengths differ!".to_string(),
));
}
let temp_buffer = cast_slice_mut::<u8, u16>(buffer);
let factor = match self.config().mode {
ConvertMode::Scaled => {
u16::MAX / (u8::MAX as u16)
}
ConvertMode::Clipped => {
1_u16
}
ConvertMode::Scaled => u16::MAX / (u8::MAX as u16),
ConvertMode::Clipped => 1_u16,
};
to_decode.buffer().iter().duplicate_const::<3>().arrays::<3>().flat_map(|pixel| {
to_decode
.buffer()
.iter()
.duplicate_const::<3>()
.arrays::<3>()
.flat_map(|pixel| {
let px_r = (*pixel[0_usize] as u16) * r_u16 * factor;
let px_g = (*pixel[1_usize] as u16) * g_u16 * factor;
let px_b = (*pixel[2_usize] as u16) * b_u16 * factor;
let px_a = u16::MAX * a_u16;
[px_r, px_g, px_b, px_a]
}).enumerate().for_each(|(len, data)| {
unsafe {
})
.enumerate()
.for_each(|(len, data)| unsafe {
*temp_buffer.get_unchecked_mut(len) = data;
}
}); Ok(())
});
Ok(())
}
PixelDestination::Luma16 => {
let buffer_u16 = cast_slice_mut::<u8, u16>(buffer);
if to_decode.len() != buffer_u16.len() {
return Err(NokhwaError::DecoderInvalidBuffer("Lengths differ!".to_string()))
return Err(NokhwaError::DecoderInvalidBuffer(
"Lengths differ!".to_string(),
));
}
let factor = match self.config().mode {
@@ -199,22 +231,24 @@ impl Decoder for LumaDecoder {
ConvertMode::Clipped => 0,
};
to_decode.buffer().iter().map(|px| {
(*px as u16) << factor
}).enumerate()
.for_each(|(len, data)| {
unsafe {
to_decode
.buffer()
.iter()
.map(|px| (*px as u16) << factor)
.enumerate()
.for_each(|(len, data)| unsafe {
*buffer_u16.get_unchecked_mut(len) = data;
}
}); Ok(())
});
Ok(())
}
PixelDestination::LumaA16 => {
let default_alpha = u16::MAX * a_u16;
let buffer_u16 = cast_slice_mut::<u8, u16>(buffer);
if (to_decode.len() * 2) != buffer_u16.len() {
return Err(NokhwaError::DecoderInvalidBuffer("Lengths differ!".to_string()))
return Err(NokhwaError::DecoderInvalidBuffer(
"Lengths differ!".to_string(),
));
}
let factor = match self.config().mode {
@@ -222,48 +256,72 @@ impl Decoder for LumaDecoder {
ConvertMode::Clipped => 0,
};
to_decode.buffer().iter().map(|px| {
(*px as u16) << factor
}).interweave::<0>(default_alpha, false).enumerate()
.for_each(|(len, data)| {
unsafe {
to_decode
.buffer()
.iter()
.map(|px| (*px as u16) << factor)
.interweave::<0>(default_alpha, false)
.enumerate()
.for_each(|(len, data)| unsafe {
*buffer_u16.get_unchecked_mut(len) = data;
});
Ok(())
}
}); Ok(())
}
fmt => Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat(fmt))
}
}
FrameFormat::Luma_10 => convert_u16_type_buffers(to_decode, buffer, destination_format, self.config().mode, self.config().channel_filters, 10),
FrameFormat::Luma_12 => convert_u16_type_buffers(to_decode, buffer, destination_format, self.config().mode, self.config().channel_filters, 12),
FrameFormat::Luma_14 => convert_u16_type_buffers(to_decode, buffer, destination_format, self.config().mode, self.config().channel_filters, 14),
FrameFormat::Luma_16 | FrameFormat::Depth_16 => convert_u16_type_buffers(to_decode, buffer, destination_format, self.config().mode, self.config().channel_filters, 16),
fmt => {
Err(NokhwaError::DecoderUnsupportedFrameFormat(fmt))
}
fmt => Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat(fmt)),
},
FrameFormat::Luma_10 => convert_u16_type_buffers(
to_decode,
buffer,
destination_format,
self.config().mode,
self.config().channel_filters,
10,
),
FrameFormat::Luma_12 => convert_u16_type_buffers(
to_decode,
buffer,
destination_format,
self.config().mode,
self.config().channel_filters,
12,
),
FrameFormat::Luma_14 => convert_u16_type_buffers(
to_decode,
buffer,
destination_format,
self.config().mode,
self.config().channel_filters,
14,
),
FrameFormat::Luma_16 | FrameFormat::Depth_16 => convert_u16_type_buffers(
to_decode,
buffer,
destination_format,
self.config().mode,
self.config().channel_filters,
16,
),
fmt => Err(NokhwaError::DecoderUnsupportedFrameFormat(fmt)),
}
}
}
fn filter_to_u8(filter: bool) -> u8 {
if filter {
1_u8
} else {
0_u8
if filter { 1_u8 } else { 0_u8 }
}
}
fn filter_to_u16(filter: bool) -> u16 {
if filter {
1_u16
} else {
0_u16
}
if filter { 1_u16 } else { 0_u16 }
}
fn convert_u16_type_buffers(to_decode: FrameBuffer, destination: &mut [u8], hint: PixelDestination, mode: ConvertMode, channel_filters: ChannelFilters, original_bit_num: u32) -> Result<(), NokhwaError> {
fn convert_u16_type_buffers(
to_decode: FrameBuffer,
destination: &mut [u8],
hint: PixelDestination,
mode: ConvertMode,
channel_filters: ChannelFilters,
original_bit_num: u32,
) -> Result<(), NokhwaError> {
let r_u16 = filter_to_u16(channel_filters.red);
let g_u16 = filter_to_u16(channel_filters.green);
let b_u16 = filter_to_u16(channel_filters.blue);
@@ -279,23 +337,22 @@ fn convert_u16_type_buffers(to_decode: FrameBuffer, destination: &mut [u8], hint
let to_decode_u16 = cast_slice::<u8, u16>(to_decode.buffer());
if to_decode_u16.len() != destination.len() {
return Err(NokhwaError::DecoderInvalidBuffer("sizes differ!".to_string()))
return Err(NokhwaError::DecoderInvalidBuffer(
"sizes differ!".to_string(),
));
}
let factor = match mode {
ConvertMode::Scaled => {
original_bit_num - 8_u32
}
ConvertMode::Scaled => original_bit_num - 8_u32,
ConvertMode::Clipped => 0,
};
to_decode_u16.iter().map(|px| {
(*px >> factor) as u8
}).enumerate()
.for_each(|(len, data)| {
unsafe {
to_decode_u16
.iter()
.map(|px| (*px >> factor) as u8)
.enumerate()
.for_each(|(len, data)| unsafe {
*destination.get_unchecked_mut(len) = data;
}
});
Ok(())
}
@@ -303,23 +360,23 @@ fn convert_u16_type_buffers(to_decode: FrameBuffer, destination: &mut [u8], hint
let to_decode_u16 = cast_slice::<u8, u16>(to_decode.buffer());
if (to_decode_u16.len() * 2) != destination.len() {
return Err(NokhwaError::DecoderInvalidBuffer("sizes differ!".to_string()))
return Err(NokhwaError::DecoderInvalidBuffer(
"sizes differ!".to_string(),
));
}
let factor = match mode {
ConvertMode::Scaled => {
original_bit_num - 8_u32
}
ConvertMode::Scaled => original_bit_num - 8_u32,
ConvertMode::Clipped => 0,
};
to_decode_u16.iter().map(|px| {
(*px >> factor) as u8
}).interweave::<0>(255 * a, false).enumerate()
.for_each(|(len, data)| {
unsafe {
to_decode_u16
.iter()
.map(|px| (*px >> factor) as u8)
.interweave::<0>(255 * a, false)
.enumerate()
.for_each(|(len, data)| unsafe {
*destination.get_unchecked_mut(len) = data;
}
});
Ok(())
}
@@ -327,25 +384,29 @@ fn convert_u16_type_buffers(to_decode: FrameBuffer, destination: &mut [u8], hint
let to_decode_u16 = cast_slice::<u8, u16>(to_decode.buffer());
if (to_decode_u16.len() * 3) != destination.len() {
return Err(NokhwaError::DecoderInvalidBuffer("sizes differ!".to_string()))
return Err(NokhwaError::DecoderInvalidBuffer(
"sizes differ!".to_string(),
));
}
let factor = match mode {
ConvertMode::Scaled => {
original_bit_num - 8_u32
}
ConvertMode::Scaled => original_bit_num - 8_u32,
ConvertMode::Clipped => 0,
};
to_decode_u16.iter().duplicate_const::<3>().arrays::<3>().flat_map(|px| {
to_decode_u16
.iter()
.duplicate_const::<3>()
.arrays::<3>()
.flat_map(|px| {
let px_r = (*px[0_usize] >> factor) * r_u16;
let px_g = (*px[1_usize] >> factor) * g_u16;
let px_b = (*px[2_usize] >> factor) * b_u16;
[px_r as u8, px_g as u8, px_b as u8]
}).enumerate().for_each(|(len, data)| {
unsafe {
})
.enumerate()
.for_each(|(len, data)| unsafe {
*destination.get_unchecked_mut(len) = data;
}
});
Ok(())
}
@@ -353,26 +414,30 @@ fn convert_u16_type_buffers(to_decode: FrameBuffer, destination: &mut [u8], hint
let to_decode_u16 = cast_slice::<u8, u16>(to_decode.buffer());
if (to_decode_u16.len() * 4) != destination.len() {
return Err(NokhwaError::DecoderInvalidBuffer("sizes differ!".to_string()))
return Err(NokhwaError::DecoderInvalidBuffer(
"sizes differ!".to_string(),
));
}
let factor = match mode {
ConvertMode::Scaled => {
original_bit_num - 8_u32
}
ConvertMode::Scaled => original_bit_num - 8_u32,
ConvertMode::Clipped => 0,
};
to_decode_u16.iter().duplicate_const::<3>().arrays::<3>().flat_map(|px| {
to_decode_u16
.iter()
.duplicate_const::<3>()
.arrays::<3>()
.flat_map(|px| {
let px_r = (*px[0_usize] >> factor) * r_u16;
let px_g = (*px[1_usize] >> factor) * g_u16;
let px_b = (*px[2_usize] >> factor) * b_u16;
let px_a = u8::MAX * a;
[px_r as u8, px_g as u8, px_b as u8, px_a]
}).enumerate().for_each(|(len, data)| {
unsafe {
})
.enumerate()
.for_each(|(len, data)| unsafe {
*destination.get_unchecked_mut(len) = data;
}
});
Ok(())
}
@@ -381,25 +446,29 @@ fn convert_u16_type_buffers(to_decode: FrameBuffer, destination: &mut [u8], hint
let destination_buffer_u16 = cast_slice_mut::<u8, u16>(destination);
if (to_decode_u16.len() * 3) != destination_buffer_u16.len() {
return Err(NokhwaError::DecoderInvalidBuffer("sizes differ!".to_string()))
return Err(NokhwaError::DecoderInvalidBuffer(
"sizes differ!".to_string(),
));
}
let factor = match mode {
ConvertMode::Scaled => {
original_bit_num - 8_u32
}
ConvertMode::Scaled => original_bit_num - 8_u32,
ConvertMode::Clipped => 0,
};
to_decode_u16.iter().duplicate_const::<3>().arrays::<3>().flat_map(|px| {
to_decode_u16
.iter()
.duplicate_const::<3>()
.arrays::<3>()
.flat_map(|px| {
let px_r = (*px[0_usize] >> factor) * r_u16;
let px_g = (*px[1_usize] >> factor) * g_u16;
let px_b = (*px[2_usize] >> factor) * b_u16;
[px_r, px_g, px_b]
}).enumerate().for_each(|(len, data)| {
unsafe {
})
.enumerate()
.for_each(|(len, data)| unsafe {
*destination_buffer_u16.get_unchecked_mut(len) = data;
}
});
Ok(())
}
@@ -408,63 +477,68 @@ fn convert_u16_type_buffers(to_decode: FrameBuffer, destination: &mut [u8], hint
let destination_buffer_u16 = cast_slice_mut::<u8, u16>(destination);
if (to_decode_u16.len() * 3) != destination_buffer_u16.len() {
return Err(NokhwaError::DecoderInvalidBuffer("sizes differ!".to_string()))
return Err(NokhwaError::DecoderInvalidBuffer(
"sizes differ!".to_string(),
));
}
let factor = match mode {
ConvertMode::Scaled => {
original_bit_num - 8_u32
}
ConvertMode::Scaled => original_bit_num - 8_u32,
ConvertMode::Clipped => 0,
};
to_decode_u16.iter().duplicate_const::<3>().arrays::<3>().flat_map(|px| {
to_decode_u16
.iter()
.duplicate_const::<3>()
.arrays::<3>()
.flat_map(|px| {
let px_r = (*px[0_usize] >> factor) * r_u16;
let px_g = (*px[1_usize] >> factor) * g_u16;
let px_b = (*px[2_usize] >> factor) * b_u16;
let px_a = u16::MAX * a_u16;
[px_r, px_g, px_b, px_a]
}).enumerate().for_each(|(len, data)| {
unsafe {
})
.enumerate()
.for_each(|(len, data)| unsafe {
*destination_buffer_u16.get_unchecked_mut(len) = data;
}
});
Ok(())
}
PixelDestination::Luma16 => {
if to_decode.len() != destination.len() {
return Err(NokhwaError::DecoderInvalidBuffer("sizes differ!".to_string()))
return Err(NokhwaError::DecoderInvalidBuffer(
"sizes differ!".to_string(),
));
}
match to_decode.consume().0 {
Cow::Borrowed(data) => {
destination.copy_from_slice(data)
}
Cow::Owned(mut owned) => {
destination.swap_with_slice(owned.as_mut_slice())
}
Cow::Borrowed(data) => destination.copy_from_slice(data),
Cow::Owned(mut owned) => destination.swap_with_slice(owned.as_mut_slice()),
}
Ok(())
}
PixelDestination::LumaA16 => {
if (to_decode.len() * 2) != destination.len() {
return Err(NokhwaError::DecoderInvalidBuffer("sizes differ!".to_string()))
return Err(NokhwaError::DecoderInvalidBuffer(
"sizes differ!".to_string(),
));
}
let to_decode_u16 = cast_slice::<u8, u16>(to_decode.buffer());
let destination_buffer_u16 = cast_slice_mut::<u8, u16>(destination);
to_decode_u16.iter().interweave::<0>(&(u16::MAX * a_u16), false).enumerate().for_each(|(len, data)| {
unsafe {
to_decode_u16
.iter()
.interweave::<0>(&(u16::MAX * a_u16), false)
.enumerate()
.for_each(|(len, data)| unsafe {
*destination_buffer_u16.get_unchecked_mut(len) = *data;
}
});
Ok(())
}
_ => Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat(hint))
_ => Err(NokhwaError::DecoderUnsupportedDestinationPixelFormat(hint)),
}
}
#[derive(Clone, Debug, PartialEq)]
@@ -487,7 +561,7 @@ impl TryFrom<CameraFormat> for LumaConfig {
fn try_from(value: CameraFormat) -> Result<Self, Self::Error> {
if !FrameFormat::BRIGHTNESS_LUMA.contains(&value.format()) {
return Err(NokhwaError::DecoderUnsupportedFrameFormat(value.format()))
return Err(NokhwaError::DecoderUnsupportedFrameFormat(value.format()));
}
Ok(LumaConfig {
@@ -504,7 +578,7 @@ impl TryFrom<CameraFormat> for LumaConfig {
pub enum ConvertMode {
#[default]
Scaled,
Clipped
Clipped,
}
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
@@ -525,3 +599,9 @@ impl Default for ChannelFilters {
}
}
}
#[cfg(test)]
mod test {
#[test]
pub fn luma8() {}
}
+82 -33
View File
@@ -1,24 +1,44 @@
use nokhwa_core::decoder::{ConfigHasResolution, Decoder, ImageBuffer, Pixel};
use nokhwa_core::decoder::{ConfigHasResolution, Decoder};
use nokhwa_core::error::NokhwaError;
use nokhwa_core::frame_buffer::FrameBuffer;
use nokhwa_core::image::Primitive;
use nokhwa_core::image::{DecodedImage, NonFloatScalarWidth};
use nokhwa_core::types::Resolution;
use nokhwa_core::frame_format::FrameFormat;
use nokhwa_core::pixel_destination::PixelDestination;
use nokhwa_core::types::{CameraFormat, Resolution};
use zune_core::bytestream::ZCursor;
use zune_core::colorspace::ColorSpace;
pub use zune_core::options::DecoderOptions;
pub use zune_jpeg::ImageInfo;
use zune_jpeg::JpegDecoder;
use zune_jpeg::errors::DecodeErrors;
use nokhwa_core::pixel_destination::PixelDestination;
#[derive(Clone, Debug)]
pub struct MJpegDecoder {
config: MJpegOptions,
config: MJpegConfig,
}
impl MJpegDecoder {
pub fn new(config: MJpegConfig) -> Self {
MJpegDecoder { config }
}
pub fn from_camera_format(camera_format: CameraFormat) -> Result<Self, NokhwaError> {
let resolution = camera_format.resolution();
if camera_format.format() != FrameFormat::MJPEG {
return Err(NokhwaError::DecoderInvalidFrameData(
"Not MJPEG!".to_string(),
));
}
let decoder_options = DecoderOptions::new_safe();
let config = MJpegConfig {
resolution,
decoder_options,
};
Ok(MJpegDecoder { config })
}
}
impl Decoder for MJpegDecoder {
type Config = MJpegOptions;
type Config = MJpegConfig;
type OutputMeta = ImageMeta;
const SUPPORTED_DESTINATIONS: &'static [PixelDestination] = &[
PixelDestination::Rgb8,
@@ -49,9 +69,14 @@ impl Decoder for MJpegDecoder {
let mut decoder = JpegDecoder::new(cursor);
let colorspace = convert_destination_to_colorspace(destination_format).ok_or(NokhwaError::DecoderUnsupportedDestinationPixelFormat(destination_format))?;
let colorspace = convert_destination_to_colorspace(destination_format).ok_or(
NokhwaError::DecoderUnsupportedDestinationPixelFormat(destination_format),
)?;
let config = self.config.decoder_options.jpeg_set_out_colorspace(colorspace);
let config = self
.config
.decoder_options
.jpeg_set_out_colorspace(colorspace);
decoder.set_options(config);
decoder.decode_into(buffer).map_err(err_to_err)?;
@@ -67,28 +92,6 @@ impl Decoder for MJpegDecoder {
Ok(info.into())
}
fn decode<P: Pixel>(
&mut self,
to_decode: FrameBuffer,
) -> Result<DecodedImage<P, Self::OutputMeta>, NokhwaError>
where
<P as Pixel>::Subpixel: NonFloatScalarWidth,
{
let min_size = self.output_decoder_min_size_pixel::<P>(self.config.resolution)?;
let mut out_buffer: Vec<P::Subpixel> = vec![P::Subpixel::DEFAULT_MAX_VALUE; min_size];
let output_metadata =
self.decode_to_pixel_buffer::<P>(to_decode, out_buffer.as_mut_slice())?;
let image_buffer = ImageBuffer::from_raw(
output_metadata.resolution.width(),
output_metadata.resolution.height(),
out_buffer,
)
.ok_or(NokhwaError::Decoder(
"Failed to make imagebuffer".to_string(),
))?;
Ok(DecodedImage::new(image_buffer, output_metadata))
}
}
#[derive(Clone, Debug, PartialEq)]
@@ -115,12 +118,12 @@ impl From<ImageInfo> for ImageMeta {
}
#[derive(Copy, Clone, Debug)]
pub struct MJpegOptions {
pub struct MJpegConfig {
pub resolution: Resolution,
pub decoder_options: DecoderOptions,
}
impl ConfigHasResolution for MJpegOptions {
impl ConfigHasResolution for MJpegConfig {
fn resolution(&self) -> Resolution {
self.resolution
}
@@ -170,3 +173,49 @@ fn convert_destination_to_colorspace(pixel_destination: PixelDestination) -> Opt
_ => None,
}
}
#[cfg(test)]
mod test {
use std::{
fs::File,
io::{BufReader, Read},
};
use image::{DynamicImage, ImageFormat, Rgb};
use nokhwa_core::{decoder::Decoder, frame_buffer::FrameBuffer, types::Resolution};
use zune_core::options::DecoderOptions;
use crate::mjpeg::{MJpegConfig, MJpegDecoder};
fn load_image(filename: String, format: ImageFormat) -> DynamicImage {
let file = File::open(filename).unwrap();
let image = image::load(BufReader::new(file), format).unwrap();
image
}
#[test]
pub fn decode_mjpeg_rgb8() {
let mut source_file = File::open("test_images/mjpeg/iwillquit.mjpeg").unwrap();
let mut data = Vec::new();
let test_image = load_image(
"test_images/mjpeg/iwillquit.rgb8.png".to_string(),
ImageFormat::Png,
)
.to_rgb8();
source_file.read_to_end(&mut data).unwrap();
let resolution = Resolution::new(1044, 409);
let decoder_options = DecoderOptions::new_fast();
let config = MJpegConfig {
resolution,
decoder_options,
};
let decode_buffer = FrameBuffer::from(data);
let mut decoder = MJpegDecoder::new(config);
let out = decoder.decode::<Rgb<u8>>(decode_buffer).unwrap();
assert_eq!(out.as_raw(), test_image.as_raw());
}
}
+591 -174
View File
@@ -1,10 +1,11 @@
use std::collections::HashMap;
use bytemuck::{cast_slice, cast_slice_mut};
use std::collections::HashMap;
use nokhwa_core::decoder::{ConfigHasResolution, Decoder};
use nokhwa_core::error::NokhwaError;
use nokhwa_core::frame_buffer::FrameBuffer;
use nokhwa_core::frame_format::{CustomFrameFormat, FrameFormat};
use nokhwa_core::pixel_destination::PixelDestination;
use nokhwa_core::types::{CameraFormat, Resolution};
use yuv::{
YuvBiPlanarImage, YuvConversionMode, YuvPackedImage, YuvPlanarImage, YuvRange,
@@ -21,7 +22,6 @@ use yuv::{
yuyv422_to_rgb, yuyv422_to_rgb_p16, yuyv422_to_rgba, yuyv422_to_rgba_p16, yvyu422_to_bgr,
yvyu422_to_bgra, yvyu422_to_rgb, yvyu422_to_rgb_p16, yvyu422_to_rgba, yvyu422_to_rgba_p16,
};
use nokhwa_core::pixel_destination::PixelDestination;
pub struct YUVDecoder {
config: YUVConfig,
@@ -53,10 +53,13 @@ impl Decoder for YUVDecoder {
}
if let Some(custom_map) = &config.custom_frame_format_map
&& let Some((src, dest)) = custom_map.iter().find(|(_, value)| {
FrameFormat::YCBCR.contains(value)
}) {
return Err(NokhwaError::DecoderUnsupportedCustomFrameFormatDestination(*src, *dest))
&& let Some((src, dest)) = custom_map
.iter()
.find(|(_, value)| FrameFormat::YCBCR.contains(value))
{
return Err(NokhwaError::DecoderUnsupportedCustomFrameFormatDestination(
*src, *dest,
));
}
self.config = config;
@@ -69,27 +72,29 @@ impl Decoder for YUVDecoder {
mut buffer: impl AsMut<[u8]>,
destination_format: PixelDestination,
) -> Result<Self::OutputMeta, NokhwaError> {
let format = self.config().custom_frame_format_map.as_ref().and_then(|m| {
match self.config().yuv_type {
FrameFormat::Custom(cfmt) => {
m.get(&cfmt).copied()
}
let format = self
.config()
.custom_frame_format_map
.as_ref()
.and_then(|m| match self.config().yuv_type {
FrameFormat::Custom(cfmt) => m.get(&cfmt).copied(),
_ => None,
}
}).unwrap_or(self.config().yuv_type);
})
.unwrap_or(self.config().yuv_type);
let buffer = buffer.as_mut();
if buffer.len() < self.output_decoder_min_size(self.config.resolution, destination_format)? {
if buffer.len()
< self.output_decoder_min_size(self.config.resolution, destination_format)?
{
return Err(NokhwaError::DecoderInvalidBuffer("Too small!".to_string()));
}
let stride = figure_out_stride(format).ok_or(NokhwaError::DecoderUnsupportedFrameFormat(format))?;
let byte_width = figure_out_byte_width(format).ok_or(NokhwaError::DecoderUnsupportedFrameFormat(format))?;
let stride =
figure_out_stride(format).ok_or(NokhwaError::DecoderUnsupportedFrameFormat(format))?;
let byte_width = figure_out_byte_width(format)
.ok_or(NokhwaError::DecoderUnsupportedFrameFormat(format))?;
let stride_3px = 3 *
self.config.resolution.width();
let stride_3px = 3 * self.config.resolution.width();
let stride_4px = 4 * self.config.resolution.width();
let stride_3px_2w = 6 * self.config.resolution.width();
let stride_4px_2w = 8 * self.config.resolution.width();
@@ -99,169 +104,573 @@ impl Decoder for YUVDecoder {
Stride::Packed(stride) => {
let image = prepare_to_packed_image(&to_decode, self.config.resolution, stride);
match format {
FrameFormat::Ayuv_32 => {
match destination_format {
PixelDestination::Rgb8 => Some(ayuv_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.premultiply_alpha)),
PixelDestination::Rgba8 => Some(ayuv_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.premultiply_alpha)),
FrameFormat::Ayuv_32 => match destination_format {
PixelDestination::Rgb8 => Some(ayuv_to_rgb(
&image,
buffer,
stride_3px,
self.config.range,
self.config.matrix,
self.config.premultiply_alpha,
)),
PixelDestination::Rgba8 => Some(ayuv_to_rgba(
&image,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
self.config.premultiply_alpha,
)),
_ => None,
}
}
FrameFormat::Yuyv_4_2_2 => {
match destination_format {
PixelDestination::Rgb8 => Some(yuyv422_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)),
PixelDestination::Rgba8 => Some(yuyv422_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)),
PixelDestination::Rgb16 => Some(yuyv422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)),
PixelDestination::Rgba16 => Some(yuyv422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)),
PixelDestination::Bgr8 => Some(yuyv422_to_bgr(&image, buffer, stride_4px_2w, self.config.range, self.config.matrix)),
PixelDestination::Bgra8 => Some(yuyv422_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)),
},
FrameFormat::Yuyv_4_2_2 => match destination_format {
PixelDestination::Rgb8 => Some(yuyv422_to_rgb(
&image,
buffer,
stride_3px,
self.config.range,
self.config.matrix,
)),
PixelDestination::Rgba8 => Some(yuyv422_to_rgba(
&image,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
)),
PixelDestination::Rgb16 => Some(yuyv422_to_rgb_p16(
&convert_packed_image_to_u16(image),
cast_slice_mut(buffer),
stride_3px_2w,
16,
self.config.range,
self.config.matrix,
)),
PixelDestination::Rgba16 => Some(yuyv422_to_rgba_p16(
&convert_packed_image_to_u16(image),
cast_slice_mut(buffer),
stride_3px_2w,
16,
self.config.range,
self.config.matrix,
)),
PixelDestination::Bgr8 => Some(yuyv422_to_bgr(
&image,
buffer,
stride_4px_2w,
self.config.range,
self.config.matrix,
)),
PixelDestination::Bgra8 => Some(yuyv422_to_bgra(
&image,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
)),
_ => None,
}
}
FrameFormat::Uyvy_4_2_2 => {
match destination_format {
PixelDestination::Rgb8 => Some(uyvy422_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)),
PixelDestination::Rgba8 => Some(uyvy422_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)),
PixelDestination::Rgb16 => Some(uyvy422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)),
PixelDestination::Rgba16 => Some(uyvy422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)),
PixelDestination::Bgr8 => Some(uyvy422_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix)),
PixelDestination::Bgra8 => Some(uyvy422_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)),
},
FrameFormat::Uyvy_4_2_2 => match destination_format {
PixelDestination::Rgb8 => Some(uyvy422_to_rgb(
&image,
buffer,
stride_3px,
self.config.range,
self.config.matrix,
)),
PixelDestination::Rgba8 => Some(uyvy422_to_rgba(
&image,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
)),
PixelDestination::Rgb16 => Some(uyvy422_to_rgb_p16(
&convert_packed_image_to_u16(image),
cast_slice_mut(buffer),
stride_3px_2w,
16,
self.config.range,
self.config.matrix,
)),
PixelDestination::Rgba16 => Some(uyvy422_to_rgba_p16(
&convert_packed_image_to_u16(image),
cast_slice_mut(buffer),
stride_3px_2w,
16,
self.config.range,
self.config.matrix,
)),
PixelDestination::Bgr8 => Some(uyvy422_to_bgr(
&image,
buffer,
stride_3px,
self.config.range,
self.config.matrix,
)),
PixelDestination::Bgra8 => Some(uyvy422_to_bgra(
&image,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
)),
_ => None,
}
}
FrameFormat::Vyuy_4_2_2 => {
match destination_format {
PixelDestination::Rgb8 => Some(vyuy422_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)),
PixelDestination::Rgba8 => Some(vyuy422_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)),
PixelDestination::Rgb16 => Some(vyuy422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)),
PixelDestination::Rgba16 => Some(vyuy422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)),
PixelDestination::Bgr8 => Some(vyuy422_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix)),
PixelDestination::Bgra8 => Some(vyuy422_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)),
},
FrameFormat::Vyuy_4_2_2 => match destination_format {
PixelDestination::Rgb8 => Some(vyuy422_to_rgb(
&image,
buffer,
stride_3px,
self.config.range,
self.config.matrix,
)),
PixelDestination::Rgba8 => Some(vyuy422_to_rgba(
&image,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
)),
PixelDestination::Rgb16 => Some(vyuy422_to_rgb_p16(
&convert_packed_image_to_u16(image),
cast_slice_mut(buffer),
stride_3px_2w,
16,
self.config.range,
self.config.matrix,
)),
PixelDestination::Rgba16 => Some(vyuy422_to_rgba_p16(
&convert_packed_image_to_u16(image),
cast_slice_mut(buffer),
stride_3px_2w,
16,
self.config.range,
self.config.matrix,
)),
PixelDestination::Bgr8 => Some(vyuy422_to_bgr(
&image,
buffer,
stride_3px,
self.config.range,
self.config.matrix,
)),
PixelDestination::Bgra8 => Some(vyuy422_to_bgra(
&image,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
)),
_ => None,
}
}
FrameFormat::Yvyu_4_2_2 => {
match destination_format {
PixelDestination::Rgb8 => Some(yvyu422_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)),
PixelDestination::Rgba8 => Some(yvyu422_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)),
PixelDestination::Rgb16 => Some(yvyu422_to_rgb_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)),
PixelDestination::Rgba16 => Some(yvyu422_to_rgba_p16(&convert_packed_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, 16, self.config.range, self.config.matrix)),
PixelDestination::Bgr8 => Some(yvyu422_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix)),
PixelDestination::Bgra8 => Some(yvyu422_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)),
},
FrameFormat::Yvyu_4_2_2 => match destination_format {
PixelDestination::Rgb8 => Some(yvyu422_to_rgb(
&image,
buffer,
stride_3px,
self.config.range,
self.config.matrix,
)),
PixelDestination::Rgba8 => Some(yvyu422_to_rgba(
&image,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
)),
PixelDestination::Rgb16 => Some(yvyu422_to_rgb_p16(
&convert_packed_image_to_u16(image),
cast_slice_mut(buffer),
stride_3px_2w,
16,
self.config.range,
self.config.matrix,
)),
PixelDestination::Rgba16 => Some(yvyu422_to_rgba_p16(
&convert_packed_image_to_u16(image),
cast_slice_mut(buffer),
stride_3px_2w,
16,
self.config.range,
self.config.matrix,
)),
PixelDestination::Bgr8 => Some(yvyu422_to_bgr(
&image,
buffer,
stride_3px,
self.config.range,
self.config.matrix,
)),
PixelDestination::Bgra8 => Some(yvyu422_to_bgra(
&image,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
)),
_ => None,
}
}
},
_ => {
if FrameFormat::YCBCR_PACKED.contains(&self.config.yuv_type) {
return Err(NokhwaError::NotImplementedError("etto blehhh! ()".to_string()))
return Err(NokhwaError::NotImplementedError(
"etto blehhh! ()".to_string(),
));
}
// shouldnt happen
return Err(NokhwaError::DecoderUnsupportedFrameFormat(format))
return Err(NokhwaError::DecoderUnsupportedFrameFormat(format));
}
}
}
Stride::Semi(y_stride, uv_stride) => {
let image = prepare_to_semi_planar_image(&to_decode, self.config.resolution, byte_width, y_stride, uv_stride);
let image = prepare_to_semi_planar_image(
&to_decode,
self.config.resolution,
byte_width,
y_stride,
uv_stride,
);
match format {
FrameFormat::NV24 => {
match destination_format {
PixelDestination::Rgb8 => Some(yuv_nv24_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
PixelDestination::Rgba8 => Some(yuv_nv24_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
PixelDestination::Bgr8 => Some(yuv_nv24_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
PixelDestination::Bgra8 => Some(yuv_nv24_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
FrameFormat::NV24 => match destination_format {
PixelDestination::Rgb8 => Some(yuv_nv24_to_rgb(
&image,
buffer,
stride_3px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
PixelDestination::Rgba8 => Some(yuv_nv24_to_rgba(
&image,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
PixelDestination::Bgr8 => Some(yuv_nv24_to_bgr(
&image,
buffer,
stride_3px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
PixelDestination::Bgra8 => Some(yuv_nv24_to_bgra(
&image,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
_ => None,
}
}
FrameFormat::NV42 => {
match destination_format {
PixelDestination::Rgb8 => Some(yuv_nv42_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
PixelDestination::Rgba8 => Some(yuv_nv42_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
PixelDestination::Bgr8 => Some(yuv_nv42_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
PixelDestination::Bgra8 => Some(yuv_nv42_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
},
FrameFormat::NV42 => match destination_format {
PixelDestination::Rgb8 => Some(yuv_nv42_to_rgb(
&image,
buffer,
stride_3px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
PixelDestination::Rgba8 => Some(yuv_nv42_to_rgba(
&image,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
PixelDestination::Bgr8 => Some(yuv_nv42_to_bgr(
&image,
buffer,
stride_3px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
PixelDestination::Bgra8 => Some(yuv_nv42_to_bgra(
&image,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
_ => None,
}
}
FrameFormat::NV16 => {
match destination_format {
PixelDestination::Rgb8 => Some(yuv_nv16_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
PixelDestination::Rgba8 => Some(yuv_nv16_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
PixelDestination::Bgr8 => Some(yuv_nv16_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
PixelDestination::Bgra8 => Some(yuv_nv16_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
},
FrameFormat::NV16 => match destination_format {
PixelDestination::Rgb8 => Some(yuv_nv16_to_rgb(
&image,
buffer,
stride_3px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
PixelDestination::Rgba8 => Some(yuv_nv16_to_rgba(
&image,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
PixelDestination::Bgr8 => Some(yuv_nv16_to_bgr(
&image,
buffer,
stride_3px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
PixelDestination::Bgra8 => Some(yuv_nv16_to_bgra(
&image,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
_ => None,
}
}
FrameFormat::NV61 => {
match destination_format {
PixelDestination::Rgb8 => Some(yuv_nv61_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
PixelDestination::Rgba8 => Some(yuv_nv61_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
PixelDestination::Bgr8 => Some(yuv_nv61_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
PixelDestination::Bgra8 => Some(yuv_nv61_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
},
FrameFormat::NV61 => match destination_format {
PixelDestination::Rgb8 => Some(yuv_nv61_to_rgb(
&image,
buffer,
stride_3px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
PixelDestination::Rgba8 => Some(yuv_nv61_to_rgba(
&image,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
PixelDestination::Bgr8 => Some(yuv_nv61_to_bgr(
&image,
buffer,
stride_3px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
PixelDestination::Bgra8 => Some(yuv_nv61_to_bgra(
&image,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
_ => None,
}
}
FrameFormat::NV12 => {
match destination_format {
PixelDestination::Rgb8 => Some(yuv_nv12_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
PixelDestination::Rgba8 => Some(yuv_nv12_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
PixelDestination::Bgr8 => Some(yuv_nv12_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
PixelDestination::Bgra8 => Some(yuv_nv12_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
},
FrameFormat::NV12 => match destination_format {
PixelDestination::Rgb8 => Some(yuv_nv12_to_rgb(
&image,
buffer,
stride_3px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
PixelDestination::Rgba8 => Some(yuv_nv12_to_rgba(
&image,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
PixelDestination::Bgr8 => Some(yuv_nv12_to_bgr(
&image,
buffer,
stride_3px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
PixelDestination::Bgra8 => Some(yuv_nv12_to_bgra(
&image,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
_ => None,
}
}
FrameFormat::NV21 => {
match destination_format {
PixelDestination::Rgb8 => Some(yuv_nv21_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
PixelDestination::Rgba8 => Some(yuv_nv21_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
PixelDestination::Bgr8 => Some(yuv_nv21_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
PixelDestination::Bgra8 => Some(yuv_nv21_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
},
FrameFormat::NV21 => match destination_format {
PixelDestination::Rgb8 => Some(yuv_nv21_to_rgb(
&image,
buffer,
stride_3px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
PixelDestination::Rgba8 => Some(yuv_nv21_to_rgba(
&image,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
PixelDestination::Bgr8 => Some(yuv_nv21_to_bgr(
&image,
buffer,
stride_3px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
PixelDestination::Bgra8 => Some(yuv_nv21_to_bgra(
&image,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
_ => None,
}
}
},
FrameFormat::P010 => {
let a = convert_bi_planar_image_to_u16(image);
match destination_format {
PixelDestination::Rgb8 => Some(p010_to_rgb(&a, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
PixelDestination::Rgba8 => Some(p010_to_rgba(&a, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
PixelDestination::Bgr8 => Some(p010_to_bgr(&a, buffer, stride_3px, self.config.range, self.config.matrix, self.config.mode)),
PixelDestination::Bgra8 => Some(p010_to_bgra(&a, buffer, stride_4px, self.config.range, self.config.matrix, self.config.mode)),
PixelDestination::Rgb16 => Some(p010_to_rgb10(&a, cast_slice_mut(buffer), stride_3px_2w, self.config.range, self.config.matrix)),
PixelDestination::Rgba16 => Some(p010_to_rgba10(&a, cast_slice_mut(buffer), stride_4px, self.config.range, self.config.matrix)),
PixelDestination::Rgb8 => Some(p010_to_rgb(
&a,
buffer,
stride_3px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
PixelDestination::Rgba8 => Some(p010_to_rgba(
&a,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
PixelDestination::Bgr8 => Some(p010_to_bgr(
&a,
buffer,
stride_3px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
PixelDestination::Bgra8 => Some(p010_to_bgra(
&a,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
self.config.mode,
)),
PixelDestination::Rgb16 => Some(p010_to_rgb10(
&a,
cast_slice_mut(buffer),
stride_3px_2w,
self.config.range,
self.config.matrix,
)),
PixelDestination::Rgba16 => Some(p010_to_rgba10(
&a,
cast_slice_mut(buffer),
stride_4px,
self.config.range,
self.config.matrix,
)),
_ => None,
}
}
FrameFormat::P012 => {
match destination_format {
PixelDestination::Rgb16 => Some(p012_to_rgb12(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), stride_3px_2w, self.config.range, self.config.matrix)),
PixelDestination::Rgba16 => Some(p012_to_rgba12(&convert_bi_planar_image_to_u16(image), cast_slice_mut(buffer), stride_4px_2w, self.config.range, self.config.matrix)),
FrameFormat::P012 => match destination_format {
PixelDestination::Rgb16 => Some(p012_to_rgb12(
&convert_bi_planar_image_to_u16(image),
cast_slice_mut(buffer),
stride_3px_2w,
self.config.range,
self.config.matrix,
)),
PixelDestination::Rgba16 => Some(p012_to_rgba12(
&convert_bi_planar_image_to_u16(image),
cast_slice_mut(buffer),
stride_4px_2w,
self.config.range,
self.config.matrix,
)),
_ => None,
}
}
},
_ => {
if FrameFormat::YCBCR_SEMI.contains(&format) {
return Err(NokhwaError::NotImplementedError("etto blehhh!".to_string()))
return Err(NokhwaError::NotImplementedError(
"etto blehhh!".to_string(),
));
}
// shouldnt happen
return Err(NokhwaError::DecoderUnsupportedFrameFormat(format))
return Err(NokhwaError::DecoderUnsupportedFrameFormat(format));
}
}
}
Stride::Planar(y_stride, u_stride, v_stride, line_ratio) => {
let image = prepare_to_planar_image(&to_decode, self.config.resolution, byte_width, y_stride, u_stride, v_stride, line_ratio);
let image = prepare_to_planar_image(
&to_decode,
self.config.resolution,
byte_width,
y_stride,
u_stride,
v_stride,
line_ratio,
);
match format {
FrameFormat::Yuv_4_2_0 => {
match destination_format {
PixelDestination::Rgb8 => Some(yuv420_to_rgb(&image, buffer, stride_3px, self.config.range, self.config.matrix)),
PixelDestination::Rgba8 => Some(yuv420_to_rgba(&image, buffer, stride_4px, self.config.range, self.config.matrix)),
PixelDestination::Bgr8 => Some(yuv420_to_bgr(&image, buffer, stride_3px, self.config.range, self.config.matrix)),
PixelDestination::Bgra8 => Some(yuv420_to_bgra(&image, buffer, stride_4px, self.config.range, self.config.matrix)),
FrameFormat::Yuv_4_2_0 => match destination_format {
PixelDestination::Rgb8 => Some(yuv420_to_rgb(
&image,
buffer,
stride_3px,
self.config.range,
self.config.matrix,
)),
PixelDestination::Rgba8 => Some(yuv420_to_rgba(
&image,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
)),
PixelDestination::Bgr8 => Some(yuv420_to_bgr(
&image,
buffer,
stride_3px,
self.config.range,
self.config.matrix,
)),
PixelDestination::Bgra8 => Some(yuv420_to_bgra(
&image,
buffer,
stride_4px,
self.config.range,
self.config.matrix,
)),
_ => None,
}
}
},
_ => {
if FrameFormat::YCBCR_PLANAR.contains(&format) {
return Err(NokhwaError::NotImplementedError("etto blehhh!".to_string()))
return Err(NokhwaError::NotImplementedError(
"etto blehhh!".to_string(),
));
}
// shouldnt happen
return Err(NokhwaError::DecoderUnsupportedFrameFormat(format))
return Err(NokhwaError::DecoderUnsupportedFrameFormat(format));
}
}
}
@@ -269,9 +678,7 @@ impl Decoder for YUVDecoder {
match decode_status {
Some(Ok(_)) => Ok(()),
Some(Err(why)) => Err(NokhwaError::Decoder(why.to_string())),
None => Err(NokhwaError::DecoderUnsupportedFrameFormat(
format,
)),
None => Err(NokhwaError::DecoderUnsupportedFrameFormat(format)),
}
}
}
@@ -337,11 +744,17 @@ fn figure_out_stride(frame_format: FrameFormat) -> Option<Stride> {
fn figure_out_byte_width(format: FrameFormat) -> Option<u32> {
match format {
FrameFormat::Ayuv_32 | FrameFormat::Yuyv_4_2_2
FrameFormat::Ayuv_32
| FrameFormat::Yuyv_4_2_2
| FrameFormat::Uyvy_4_2_2
| FrameFormat::Vyuy_4_2_2
| FrameFormat::Yvyu_4_2_2 => Some(1),
FrameFormat::NV24 | FrameFormat::NV42 | FrameFormat::NV16 | FrameFormat::NV61 | FrameFormat::NV12 | FrameFormat::NV21 => Some(1),
FrameFormat::NV24
| FrameFormat::NV42
| FrameFormat::NV16
| FrameFormat::NV61
| FrameFormat::NV12
| FrameFormat::NV21 => Some(1),
FrameFormat::P010 | FrameFormat::P012 => Some(2),
FrameFormat::Yuv_4_2_0 => Some(1),
_ => None,
@@ -427,7 +840,6 @@ fn prepare_to_planar_image<'a>(
v_stride_ratio: u32,
line_ratio: u32,
) -> YuvPlanarImage<'a, u8> {
let y_stride = resolution.width() * y_stride_base;
let u_stride = y_stride / u_stride_ratio;
let v_stride = y_stride / v_stride_ratio;
@@ -480,7 +892,10 @@ fn convert_bi_planar_image_to_u16(
#[cfg(test)]
mod test {
use crate::yuv::{YUVConfig, YUVDecoder};
use image::{DynamicImage, EncodableLayout, ImageBuffer, ImageFormat, Pixel, PixelWithColorType, Rgb, Rgba};
use image::{
DynamicImage, EncodableLayout, ImageBuffer, ImageFormat, Pixel, PixelWithColorType, Rgb,
Rgba,
};
use nokhwa_core::decoder::Decoder;
use nokhwa_core::frame_buffer::FrameBuffer;
use nokhwa_core::frame_format::FrameFormat;
@@ -491,23 +906,19 @@ mod test {
use std::io::{BufReader, Read};
use yuv::{YuvConversionMode, YuvRange, YuvStandardMatrix};
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
enum PixelFormat {
Rgb8,
RgbA8,
Rgb16,
RgbA16,
}
fn base_test<P: Pixel>(filename: String, resolution: Resolution, format: FrameFormat) -> ImageBuffer<P, Vec<P::Subpixel>>
fn base_test<P: Pixel>(
filename: String,
resolution: Resolution,
format: FrameFormat,
) -> ImageBuffer<P, Vec<P::Subpixel>>
where
<P as Pixel>::Subpixel: NonFloatScalarWidth {
<P as Pixel>::Subpixel: NonFloatScalarWidth,
{
let mut file = File::open(filename).unwrap();
let mut nv12_data = Vec::new();
file.read_to_end(&mut nv12_data).unwrap();
let mut decoder = YUVDecoder::new(
YUVConfig {
let mut decoder = YUVDecoder::new(YUVConfig {
resolution,
yuv_type: format,
range: YuvRange::Full,
@@ -515,15 +926,18 @@ where
mode: YuvConversionMode::Balanced,
premultiply_alpha: false,
custom_frame_format_map: None,
}
);
});
let frame_buffer = FrameBuffer::new(Cow::Owned(nv12_data), None);
decoder.decode::<P>(frame_buffer).unwrap().buffer
}
fn write_image<P: Pixel + PixelWithColorType>(image: ImageBuffer<P, Vec<P::Subpixel>>, filename: String) where
[<P as Pixel>::Subpixel]: EncodableLayout
#[allow(dead_code)]
fn write_image<P: Pixel + PixelWithColorType>(
image: ImageBuffer<P, Vec<P::Subpixel>>,
filename: String,
) where
[<P as Pixel>::Subpixel]: EncodableLayout,
{
image.save_with_format(filename, ImageFormat::Png).unwrap();
}
@@ -550,7 +964,8 @@ where
}
// test nv12 rgba8
{
let image_rgba8 = load_image(format!("{base_filename}.rgba8.png"), img_format).to_rgba8();
let image_rgba8 =
load_image(format!("{base_filename}.rgba8.png"), img_format).to_rgba8();
let out = base_test::<Rgba<u8>>(format!("{base_filename}.yuv"), resolution, format);
@@ -595,11 +1010,11 @@ where
}
{
let image_rgba8 = load_image(format!("{base_filename}.rgba8.png"), img_format).to_rgba8();
let image_rgba8 =
load_image(format!("{base_filename}.rgba8.png"), img_format).to_rgba8();
let out = base_test::<Rgba<u8>>(format!("{base_filename}.yuv"), resolution, format);
assert_eq!(image_rgba8.as_raw(), out.as_raw());
// write_image(out, format!("{base_filename}.rgba8.png"));
}
}
@@ -618,7 +1033,8 @@ where
}
{
let image_rgba8 = load_image(format!("{base_filename}.rgba8.png"), img_format).to_rgba8();
let image_rgba8 =
load_image(format!("{base_filename}.rgba8.png"), img_format).to_rgba8();
let out = base_test::<Rgba<u8>>(format!("{base_filename}.yuv"), resolution, format);
assert_eq!(image_rgba8.as_raw(), out.as_raw());
// write_image(out, format!("{base_filename}.rgba8.png"));
@@ -640,7 +1056,8 @@ where
}
{
let image_rgba8 = load_image(format!("{base_filename}.rgba8.png"), img_format).to_rgba8();
let image_rgba8 =
load_image(format!("{base_filename}.rgba8.png"), img_format).to_rgba8();
let out = base_test::<Rgba<u8>>(format!("{base_filename}.yuv"), resolution, format);
assert_eq!(image_rgba8.as_raw(), out.as_raw());
// write_image(out, format!("{base_filename}.rgba8.png"));
@@ -669,7 +1086,8 @@ where
}
{
let image_rgba8 = load_image(format!("{base_filename}.rgba8.png"), img_format).to_rgba8();
let image_rgba8 =
load_image(format!("{base_filename}.rgba8.png"), img_format).to_rgba8();
let out = base_test::<Rgba<u8>>(format!("{base_filename}.yuv"), resolution, format);
// write_image(out, format!("{base_filename}.rgba8.png"));
assert_eq!(image_rgba8.as_raw(), out.as_raw());
@@ -690,11 +1108,10 @@ where
}
{
let image_rgba8 = load_image(format!("{base_filename}.rgba8.png"), img_format).to_rgba8();
let image_rgba8 =
load_image(format!("{base_filename}.rgba8.png"), img_format).to_rgba8();
let out = base_test::<Rgba<u8>>(format!("{base_filename}.yuv"), resolution, format);
assert_eq!(image_rgba8.as_raw(), out.as_raw());
}
}
}