FrameFormat Rework

This commit is contained in:
l1npengtul
2023-01-14 17:29:06 +09:00
parent c45d7b6991
commit 500ba629c8
3 changed files with 177 additions and 534 deletions
+84 -84
View File
@@ -16,7 +16,7 @@
use crate::{
error::NokhwaError,
pixel_format::FormatDecoder,
frame_format::FrameFormat,
types::{FrameFormat, Resolution},
};
use bytes::Bytes;
@@ -69,87 +69,87 @@ impl Buffer {
self.source_frame_format
}
/// Decodes a image with allocation using the provided [`FormatDecoder`].
/// # Errors
/// Will error when the decoding fails.
#[inline]
pub fn decode_image<F: FormatDecoder>(
&self,
) -> Result<ImageBuffer<F::Output, Vec<u8>>, NokhwaError> {
let new_data = F::write_output(self.source_frame_format, self.resolution, &self.buffer)?;
let image =
ImageBuffer::from_raw(self.resolution.width_x, self.resolution.height_y, new_data)
.ok_or(NokhwaError::ProcessFrameError {
src: self.source_frame_format,
destination: stringify!(F).to_string(),
error: "Failed to create buffer".to_string(),
})?;
Ok(image)
}
/// Decodes a image with allocation using the provided [`FormatDecoder`] into a `buffer`.
/// # Errors
/// Will error when the decoding fails, or the provided buffer is too small.
#[inline]
pub fn decode_image_to_buffer<F: FormatDecoder>(
&self,
buffer: &mut [u8],
) -> Result<(), NokhwaError> {
F::write_output_buffer(
self.source_frame_format,
self.resolution,
&self.buffer,
buffer,
)
}
/// Decodes a image with allocation using the provided [`FormatDecoder`] into a [`Mat`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html).
///
/// Note that this does a clone when creating the buffer, to decouple the lifetime of the internal data to the temporary Buffer. If you want to avoid this, please see [`decode_opencv_mat`](Self::decode_opencv_mat).
/// # Errors
/// Will error when the decoding fails, or `OpenCV` failed to create/copy the [`Mat`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html).
/// # Safety
/// This function uses `unsafe` in order to create the [`Mat`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html). Please see [`Mat::new_rows_cols_with_data`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html#method.new_rows_cols_with_data) for more.
///
/// Most notably, the `data` **must** stay in scope for the duration of the [`Mat`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html) or bad, ***bad*** things happen.
#[cfg(feature = "opencv-mat")]
#[cfg_attr(feature = "docs-features", doc(cfg(feature = "opencv-mat")))]
#[allow(clippy::cast_possible_wrap)]
pub fn decode_opencv_mat<F: FormatDecoder>(
&mut self,
) -> Result<opencv::core::Mat, NokhwaError> {
use image::Pixel;
use opencv::core::{Mat, Mat_AUTO_STEP, CV_8UC1, CV_8UC2, CV_8UC3, CV_8UC4};
let array_type = match F::Output::CHANNEL_COUNT {
1 => CV_8UC1,
2 => CV_8UC2,
3 => CV_8UC3,
4 => CV_8UC4,
_ => {
return Err(NokhwaError::ProcessFrameError {
src: FrameFormat::RAWRGB,
destination: "OpenCV Mat".to_string(),
error: "Invalid Decoder FormatDecoder Channel Count".to_string(),
})
}
};
unsafe {
// TODO: Look into removing this unnecessary copy.
let mat1 = Mat::new_rows_cols_with_data(
self.resolution.height_y as i32,
self.resolution.width_x as i32,
array_type,
self.buffer.as_ref().as_ptr().cast_mut().cast(),
Mat_AUTO_STEP,
)
.map_err(|why| NokhwaError::ProcessFrameError {
src: FrameFormat::RAWRGB,
destination: "OpenCV Mat".to_string(),
error: why.to_string(),
})?;
Ok(mat1)
}
}
// /// Decodes a image with allocation using the provided [`FormatDecoder`].
// /// # Errors
// /// Will error when the decoding fails.
// #[inline]
// pub fn decode_image<F: FormatDecoder>(
// &self,
// ) -> Result<ImageBuffer<F::Output, Vec<u8>>, NokhwaError> {
// let new_data = F::write_output(self.source_frame_format, self.resolution, &self.buffer)?;
// let image =
// ImageBuffer::from_raw(self.resolution.width_x, self.resolution.height_y, new_data)
// .ok_or(NokhwaError::ProcessFrameError {
// src: self.source_frame_format,
// destination: stringify!(F).to_string(),
// error: "Failed to create buffer".to_string(),
// })?;
// Ok(image)
// }
//
// /// Decodes a image with allocation using the provided [`FormatDecoder`] into a `buffer`.
// /// # Errors
// /// Will error when the decoding fails, or the provided buffer is too small.
// #[inline]
// pub fn decode_image_to_buffer<F: FormatDecoder>(
// &self,
// buffer: &mut [u8],
// ) -> Result<(), NokhwaError> {
// F::write_output_buffer(
// self.source_frame_format,
// self.resolution,
// &self.buffer,
// buffer,
// )
// }
// /// Decodes a image with allocation using the provided [`FormatDecoder`] into a [`Mat`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html).
// ///
// /// Note that this does a clone when creating the buffer, to decouple the lifetime of the internal data to the temporary Buffer. If you want to avoid this, please see [`decode_opencv_mat`](Self::decode_opencv_mat).
// /// # Errors
// /// Will error when the decoding fails, or `OpenCV` failed to create/copy the [`Mat`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html).
// /// # Safety
// /// This function uses `unsafe` in order to create the [`Mat`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html). Please see [`Mat::new_rows_cols_with_data`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html#method.new_rows_cols_with_data) for more.
// ///
// /// Most notably, the `data` **must** stay in scope for the duration of the [`Mat`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html) or bad, ***bad*** things happen.
// #[cfg(feature = "opencv-mat")]
// #[cfg_attr(feature = "docs-features", doc(cfg(feature = "opencv-mat")))]
// #[allow(clippy::cast_possible_wrap)]
// pub fn decode_opencv_mat<F: FormatDecoder>(
// &mut self,
// ) -> Result<opencv::core::Mat, NokhwaError> {
// use image::Pixel;
// use opencv::core::{Mat, Mat_AUTO_STEP, CV_8UC1, CV_8UC2, CV_8UC3, CV_8UC4};
//
// let array_type = match F::Output::CHANNEL_COUNT {
// 1 => CV_8UC1,
// 2 => CV_8UC2,
// 3 => CV_8UC3,
// 4 => CV_8UC4,
// _ => {
// return Err(NokhwaError::ProcessFrameError {
// src: FrameFormat::RAWRGB,
// destination: "OpenCV Mat".to_string(),
// error: "Invalid Decoder FormatDecoder Channel Count".to_string(),
// })
// }
// };
//
// unsafe {
// // TODO: Look into removing this unnecessary copy.
// let mat1 = Mat::new_rows_cols_with_data(
// self.resolution.height_y as i32,
// self.resolution.width_x as i32,
// array_type,
// self.buffer.as_ref().as_ptr().cast_mut().cast(),
// Mat_AUTO_STEP,
// )
// .map_err(|why| NokhwaError::ProcessFrameError {
// src: FrameFormat::RAWRGB,
// destination: "OpenCV Mat".to_string(),
// error: why.to_string(),
// })?;
//
// Ok(mat1)
// }
// }
}
+64 -80
View File
@@ -3,11 +3,6 @@ use std::fmt::{Display, Formatter};
use std::str::FromStr;
/// Describes a frame format (i.e. how the bytes themselves are encoded). Often called `FourCC`.
/// - YUYV is a mathematical color space. You can read more [here.](https://en.wikipedia.org/wiki/YCbCr)
/// - NV12 is same as above. Note that a partial compression (e.g. [16, 235] may be coerced to [0, 255].
/// - MJPEG is a motion-jpeg compressed frame, it allows for high frame rates.
/// - GRAY is a grayscale image format, usually for specialized cameras such as IR Cameras.
/// - RAWRGB is a Raw RGB888 format.
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub enum FrameFormat {
@@ -15,11 +10,11 @@ pub enum FrameFormat {
H265,
H264,
H263,
AVC1,
MPEG1,
MPEG2,
MPEG4,
MJPEG,
Avc1,
Mpeg1,
Mpeg2,
Mpeg4,
MJpeg,
XVid,
VP8,
VP9,
@@ -37,84 +32,73 @@ pub enum FrameFormat {
Imc2,
Imc4,
// UV
UV8,
// Grayscale Formats
Luma8,
Luma8I,
Luma10,
Luma10B,
Luma12,
Luma12I,
Luma16,
// Depth Formats
Z16,
// RGB Formats
Rgb8,
// Bayer
RgbA8,
// Custom
Custom(u128),
}
impl FrameFormat {
pub const ALL: &'static [FrameFormat] = &[
FrameFormat::H263,
FrameFormat::H264,
FrameFormat::H265,
FrameFormat::Avc1,
FrameFormat::Mpeg1,
FrameFormat::Mpeg2,
FrameFormat::Mpeg4,
FrameFormat::MJpeg,
FrameFormat::XVid,
FrameFormat::VP8,
FrameFormat::VP9,
FrameFormat::Yuv422,
FrameFormat::Uyv422,
FrameFormat::Nv12,
FrameFormat::Nv21,
FrameFormat::Yv12,
FrameFormat::Imc2,
FrameFormat::Imc4,
FrameFormat::Luma8,
FrameFormat::Rgb8,
FrameFormat::RgbA8,
];
pub const COMPRESSED: &'static [FrameFormat] = &[
FrameFormat::H263,
FrameFormat::H264,
FrameFormat::H265,
FrameFormat::Avc1,
FrameFormat::Mpeg1,
FrameFormat::Mpeg2,
FrameFormat::Mpeg4,
FrameFormat::MJpeg,
FrameFormat::XVid,
FrameFormat::VP8,
FrameFormat::VP9,
];
pub const CHROMA: &'static [FrameFormat] = &[
FrameFormat::Yuv422,
FrameFormat::Uyv422,
FrameFormat::Nv12,
FrameFormat::Nv21,
FrameFormat::Yv12,
FrameFormat::Imc2,
FrameFormat::Imc4,
];
pub const LUMA: &'static [FrameFormat] = &[FrameFormat::Luma8];
pub const RGB: &'static [FrameFormat] = &[FrameFormat::Rgb8, FrameFormat::RgbA8];
}
impl Display for FrameFormat {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
FrameFormat::MJPEG => {
write!(f, "MJPEG")
}
FrameFormat::YUYV => {
write!(f, "YUYV")
}
FrameFormat::GRAY => {
write!(f, "GRAY")
}
FrameFormat::RAWRGB => {
write!(f, "RAWRGB")
}
FrameFormat::NV12 => {
write!(f, "NV12")
}
}
write!(f, "{self:?}")
}
}
impl FromStr for FrameFormat {
type Err = NokhwaError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"MJPEG" => Ok(FrameFormat::MJPEG),
"YUYV" => Ok(FrameFormat::YUYV),
"GRAY" => Ok(FrameFormat::GRAY),
"RAWRGB" => Ok(FrameFormat::RAWRGB),
"NV12" => Ok(FrameFormat::NV12),
_ => Err(NokhwaError::StructureError {
structure: "FrameFormat".to_string(),
error: format!("No match for {s}"),
}),
}
}
}
/// Returns all the frame formats
#[must_use]
pub const fn frame_formats() -> &'static [FrameFormat] {
&[
FrameFormat::MJPEG,
FrameFormat::YUYV,
FrameFormat::NV12,
FrameFormat::GRAY,
FrameFormat::RAWRGB,
]
}
/// Returns all the color frame formats
#[must_use]
pub const fn color_frame_formats() -> &'static [FrameFormat] {
&[
FrameFormat::MJPEG,
FrameFormat::YUYV,
FrameFormat::NV12,
FrameFormat::RAWRGB,
]
}
+29 -370
View File
@@ -13,389 +13,48 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::buffer::Buffer;
use crate::error::NokhwaError;
use crate::frame_format::FrameFormat;
use crate::types::{
buf_mjpeg_to_rgb, buf_nv12_to_rgb, buf_yuyv422_to_rgb, color_frame_formats, frame_formats,
mjpeg_to_rgb, nv12_to_rgb, yuyv422_to_rgb, FrameFormat, Resolution,
buf_mjpeg_to_rgb, buf_nv12_to_rgb, buf_yuyv422_to_rgb, mjpeg_to_rgb, nv12_to_rgb,
yuyv422_to_rgb, Resolution,
};
use image::{Luma, LumaA, Pixel, Rgb, Rgba};
use image::{ImageBuffer, Luma, LumaA, Pixel, Primitive, Rgb, Rgba};
use std::fmt::Debug;
use std::ops::Deref;
/// Trait that has methods to convert raw data from the webcam to a proper raw image.
pub trait FormatDecoder: Clone + Sized + Send + Sync {
type Output: Pixel<Subpixel = u8>;
const FORMATS: &'static [FrameFormat];
pub trait FormatDecoders: Send + Sync {
const NAME: &'static str;
/// Allocates and returns a `Vec`
/// # Errors
/// If the data is malformed, or the source [`FrameFormat`] is incompatible, this will error.
fn write_output(
fcc: FrameFormat,
resolution: Resolution,
data: &[u8],
) -> Result<Vec<u8>, NokhwaError>;
const PRIMARY: FrameFormat;
/// Writes to a user provided buffer.
/// # Errors
/// If the data is malformed, the source [`FrameFormat`] is incompatible, or the user-alloted buffer is not large enough, this will error.
fn write_output_buffer(
fcc: FrameFormat,
resolution: Resolution,
data: &[u8],
dest: &mut [u8],
) -> Result<(), NokhwaError>;
const ACCEPTABLE: &'static [FrameFormat];
const PLATFORM_ACCEPTABLE: &'static [(&'static str, &'static [u128])];
type Primitive: Primitive;
type Container: Deref<Target = [Self::Primitive]>;
}
/// A Zero-Size-Type that contains the definition to convert a given image stream to an RGB888 in the [`Buffer`](crate::buffer::Buffer)'s [`.decode_image()`](crate::buffer::Buffer::decode_image)
///
/// ```.ignore
/// use image::{ImageBuffer, Rgb};
/// let image: ImageBuffer<Rgb<u8>, Vec<u8>> = buffer.to_image::<RgbFormat>();
/// ```
#[derive(Copy, Clone, Debug, Default, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub struct RgbFormat;
impl FormatDecoder for RgbFormat {
type Output = Rgb<u8>;
const FORMATS: &'static [FrameFormat] = color_frame_formats();
#[inline]
fn write_output(
fcc: FrameFormat,
resolution: Resolution,
data: &[u8],
) -> Result<Vec<u8>, NokhwaError> {
match fcc {
FrameFormat::MJPEG => mjpeg_to_rgb(data, false),
FrameFormat::YUYV => yuyv422_to_rgb(data, false),
FrameFormat::GRAY => Ok(data
.iter()
.flat_map(|x| {
let pxv = *x;
[pxv, pxv, pxv]
})
.collect()),
FrameFormat::RAWRGB => Ok(data.to_vec()),
FrameFormat::NV12 => nv12_to_rgb(resolution, data, false),
}
}
#[inline]
fn write_output_buffer(
fcc: FrameFormat,
resolution: Resolution,
data: &[u8],
dest: &mut [u8],
) -> Result<(), NokhwaError> {
match fcc {
FrameFormat::MJPEG => buf_mjpeg_to_rgb(data, dest, false),
FrameFormat::YUYV => buf_yuyv422_to_rgb(data, dest, false),
FrameFormat::GRAY => {
if dest.len() != data.len() * 3 {
return Err(NokhwaError::ProcessFrameError {
src: fcc,
destination: "Luma => RGB".to_string(),
error: "Bad buffer length".to_string(),
});
}
data.iter().enumerate().for_each(|(idx, pixel_value)| {
let index = idx * 3;
dest[index] = *pixel_value;
dest[index + 1] = *pixel_value;
dest[index + 2] = *pixel_value;
});
Ok(())
}
FrameFormat::RAWRGB => {
dest.copy_from_slice(data);
Ok(())
}
FrameFormat::NV12 => buf_nv12_to_rgb(resolution, data, dest, false),
}
}
pub trait RgbDecoder: FormatDecoders {
fn decode_rgb(&self, buffer: &Buffer) -> ImageBuffer<Rgb<Self::Primitive>, Self::Container>;
}
/// A Zero-Size-Type that contains the definition to convert a given image stream to an RGBA8888 in the [`Buffer`](crate::buffer::Buffer)'s [`.decode_image()`](crate::buffer::Buffer::decode_image)
///
/// ```.ignore
/// use image::{ImageBuffer, Rgba};
/// let image: ImageBuffer<Rgba<u8>, Vec<u8>> = buffer.to_image::<RgbAFormat>();
/// ```
#[derive(Copy, Clone, Debug, Default, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub struct RgbAFormat;
impl FormatDecoder for RgbAFormat {
type Output = Rgba<u8>;
const FORMATS: &'static [FrameFormat] = color_frame_formats();
#[inline]
fn write_output(
fcc: FrameFormat,
resolution: Resolution,
data: &[u8],
) -> Result<Vec<u8>, NokhwaError> {
match fcc {
FrameFormat::MJPEG => mjpeg_to_rgb(data, true),
FrameFormat::YUYV => yuyv422_to_rgb(data, true),
FrameFormat::GRAY => Ok(data
.iter()
.flat_map(|x| {
let pxv = *x;
[pxv, pxv, pxv, 255]
})
.collect()),
FrameFormat::RAWRGB => Ok(data
.chunks_exact(3)
.flat_map(|x| [x[0], x[1], x[2], 255])
.collect()),
FrameFormat::NV12 => nv12_to_rgb(resolution, data, true),
}
}
#[inline]
fn write_output_buffer(
fcc: FrameFormat,
resolution: Resolution,
data: &[u8],
dest: &mut [u8],
) -> Result<(), NokhwaError> {
match fcc {
FrameFormat::MJPEG => buf_mjpeg_to_rgb(data, dest, true),
FrameFormat::YUYV => buf_yuyv422_to_rgb(data, dest, true),
FrameFormat::GRAY => {
if dest.len() != data.len() * 4 {
return Err(NokhwaError::ProcessFrameError {
src: fcc,
destination: "Luma => RGBA".to_string(),
error: "Bad buffer length".to_string(),
});
}
data.iter().enumerate().for_each(|(idx, pixel_value)| {
let index = idx * 4;
dest[index] = *pixel_value;
dest[index + 1] = *pixel_value;
dest[index + 2] = *pixel_value;
dest[index + 3] = 255;
});
Ok(())
}
FrameFormat::RAWRGB => {
data.chunks_exact(3).enumerate().for_each(|(idx, px)| {
let index = idx * 4;
dest[index] = px[0];
dest[index + 1] = px[1];
dest[index + 2] = px[2];
dest[index + 3] = 255;
});
Ok(())
}
FrameFormat::NV12 => buf_nv12_to_rgb(resolution, data, dest, true),
}
}
pub trait RgbADecoder: FormatDecoders {
fn decode_rgba(&self, buffer: &Buffer) -> ImageBuffer<Rgba<Self::Primitive>, Self::Container>;
}
/// A Zero-Size-Type that contains the definition to convert a given image stream to an Luma8(Grayscale 8-bit) in the [`Buffer`](crate::buffer::Buffer)'s [`.decode_image()`](crate::buffer::Buffer::decode_image)
///
/// ```.ignore
/// use image::{ImageBuffer, Luma};
/// let image: ImageBuffer<Luma<u8>, Vec<u8>> = buffer.to_image::<LumaFormat>();
/// ```
#[derive(Copy, Clone, Debug, Default, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub struct LumaFormat;
impl FormatDecoder for LumaFormat {
type Output = Luma<u8>;
const FORMATS: &'static [FrameFormat] = frame_formats();
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_sign_loss)]
#[inline]
fn write_output(
fcc: FrameFormat,
resolution: Resolution,
data: &[u8],
) -> Result<Vec<u8>, NokhwaError> {
match fcc {
FrameFormat::MJPEG => Ok(mjpeg_to_rgb(data, false)?
.as_slice()
.chunks_exact(3)
.map(|x| {
let mut avg = 0;
x.iter().for_each(|v| avg += u16::from(*v));
(avg / 3) as u8
})
.collect()),
FrameFormat::YUYV => Ok(yuyv422_to_rgb(data, false)?
.as_slice()
.chunks_exact(3)
.map(|x| {
let mut avg = 0;
x.iter().for_each(|v| avg += u16::from(*v));
(avg / 3) as u8
})
.collect()),
FrameFormat::NV12 => Ok(nv12_to_rgb(resolution, data, false)?
.as_slice()
.chunks_exact(3)
.map(|x| {
let mut avg = 0;
x.iter().for_each(|v| avg += u16::from(*v));
(avg / 3) as u8
})
.collect()),
FrameFormat::GRAY => Ok(data.to_vec()),
FrameFormat::RAWRGB => Ok(data
.chunks(3)
.map(|px| ((i32::from(px[0]) + i32::from(px[1]) + i32::from(px[2])) / 3) as u8)
.collect()),
}
}
#[inline]
fn write_output_buffer(
fcc: FrameFormat,
_resolution: Resolution,
data: &[u8],
dest: &mut [u8],
) -> Result<(), NokhwaError> {
match fcc {
// TODO: implement!
FrameFormat::MJPEG | FrameFormat::YUYV | FrameFormat::NV12 => {
Err(NokhwaError::ProcessFrameError {
src: fcc,
destination: "Luma => RGB".to_string(),
error: "Conversion Error".to_string(),
})
}
FrameFormat::GRAY => {
data.iter().zip(dest.iter_mut()).for_each(|(pxv, d)| {
*d = *pxv;
});
Ok(())
}
FrameFormat::RAWRGB => Err(NokhwaError::ProcessFrameError {
src: fcc,
destination: "RGB => RGB".to_string(),
error: "Conversion Error".to_string(),
}),
}
}
pub trait LumaDecoder: FormatDecoders {
fn decode_luma(&self, buffer: &Buffer) -> ImageBuffer<Luma<Self::Primitive>, Self::Container>;
}
/// A Zero-Size-Type that contains the definition to convert a given image stream to an LumaA8(Grayscale 8-bit with 8-bit alpha) in the [`Buffer`](crate::buffer::Buffer)'s [`.decode_image()`](crate::buffer::Buffer::decode_image)
///
/// ```.ignore
/// use image::{ImageBuffer, LumaA};
/// let image: ImageBuffer<LumaA<u8>, Vec<u8>> = buffer.to_image::<LumaAFormat>();
/// ```
#[derive(Copy, Clone, Debug, Default, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub struct LumaAFormat;
impl FormatDecoder for LumaAFormat {
type Output = LumaA<u8>;
const FORMATS: &'static [FrameFormat] = frame_formats();
#[allow(clippy::cast_possible_truncation)]
#[inline]
fn write_output(
fcc: FrameFormat,
resolution: Resolution,
data: &[u8],
) -> Result<Vec<u8>, NokhwaError> {
match fcc {
FrameFormat::MJPEG => Ok(mjpeg_to_rgb(data, false)?
.as_slice()
.chunks_exact(3)
.flat_map(|x| {
let mut avg = 0;
x.iter().for_each(|v| avg += u16::from(*v));
[(avg / 3) as u8, 255]
})
.collect()),
FrameFormat::YUYV => Ok(yuyv422_to_rgb(data, false)?
.as_slice()
.chunks_exact(3)
.flat_map(|x| {
let mut avg = 0;
x.iter().for_each(|v| avg += u16::from(*v));
[(avg / 3) as u8, 255]
})
.collect()),
FrameFormat::NV12 => Ok(nv12_to_rgb(resolution, data, false)?
.as_slice()
.chunks_exact(3)
.flat_map(|x| {
let mut avg = 0;
x.iter().for_each(|v| avg += u16::from(*v));
[(avg / 3) as u8, 255]
})
.collect()),
FrameFormat::GRAY => Ok(data.iter().flat_map(|x| [*x, 255]).collect()),
FrameFormat::RAWRGB => Err(NokhwaError::ProcessFrameError {
src: fcc,
destination: "RGB => RGB".to_string(),
error: "Conversion Error".to_string(),
}),
}
}
#[inline]
fn write_output_buffer(
fcc: FrameFormat,
_resolution: Resolution,
data: &[u8],
dest: &mut [u8],
) -> Result<(), NokhwaError> {
match fcc {
FrameFormat::MJPEG => {
// FIXME: implement!
Err(NokhwaError::ProcessFrameError {
src: fcc,
destination: "MJPEG => LumaA".to_string(),
error: "Conversion Error".to_string(),
})
}
FrameFormat::YUYV => Err(NokhwaError::ProcessFrameError {
src: fcc,
destination: "YUYV => LumaA".to_string(),
error: "Conversion Error".to_string(),
}),
FrameFormat::NV12 => Err(NokhwaError::ProcessFrameError {
src: fcc,
destination: "NV12 => LumaA".to_string(),
error: "Conversion Error".to_string(),
}),
FrameFormat::GRAY => {
if dest.len() != data.len() * 2 {
return Err(NokhwaError::ProcessFrameError {
src: fcc,
destination: "GRAY8 => LumaA".to_string(),
error: "Conversion Error".to_string(),
});
}
data.iter()
.zip(dest.chunks_exact_mut(2))
.enumerate()
.for_each(|(idx, (pxv, d))| {
let index = idx * 2;
d[index] = *pxv;
d[index + 1] = 255;
});
Ok(())
}
FrameFormat::RAWRGB => Err(NokhwaError::ProcessFrameError {
src: fcc,
destination: "RGB => RGB".to_string(),
error: "Conversion Error".to_string(),
}),
}
}
pub trait LumaADecoder: FormatDecoders {
fn decode_luma_a(
&self,
buffer: &Buffer,
) -> ImageBuffer<LumaA<Self::Primitive>, Self::Container>;
}
// TODO: Wgpu Decoder