mirror of
https://github.com/l1npengtul/nokhwa.git
synced 2026-07-04 02:27:26 +00:00
FrameFormat Rework
This commit is contained in:
+84
-84
@@ -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)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user