mirror of
https://github.com/l1npengtul/nokhwa.git
synced 2026-07-04 02:27:26 +00:00
start wrapping up public api of 0.10
This commit is contained in:
+1
-1
@@ -78,7 +78,7 @@ version = "0.13"
|
||||
optional = true
|
||||
|
||||
[dependencies.opencv]
|
||||
version = "0.65"
|
||||
version = "0.66"
|
||||
optional = true
|
||||
|
||||
[dependencies.rgb]
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
use crate::{
|
||||
mjpeg_to_rgb, nokhwa_check, nokhwa_initialize, yuyv422_to_rgb, ApiBackend, CameraControl,
|
||||
CameraFormat, CameraInfo, CaptureBackendTrait, ControlValueSetter, FrameFormat,
|
||||
KnownCameraControl, NokhwaError, PixelFormat, Resolution,
|
||||
KnownCameraControl, NokhwaError, FormatDecoder, Resolution,
|
||||
};
|
||||
use image::{ImageBuffer, Rgb};
|
||||
use nokhwa_bindings_macos::avfoundation::{
|
||||
@@ -269,7 +269,7 @@ impl CaptureBackendTrait for AVFoundationCaptureDevice {
|
||||
Ok(image_buf)
|
||||
}
|
||||
|
||||
fn frame_typed<F: PixelFormat>(
|
||||
fn frame_typed<F: FormatDecoder>(
|
||||
&mut self,
|
||||
) -> Result<ImageBuffer<crate::pixel_format::Output, Vec<u8>>, NokhwaError> {
|
||||
todo!()
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use crate::pixel_format::PixelFormat;
|
||||
use crate::pixel_format::FormatDecoder;
|
||||
use crate::{
|
||||
ApiBackend, CameraControl, CameraFormat, CameraInfo, CaptureBackendTrait, FrameFormat,
|
||||
KnownCameraControl, NokhwaError, Resolution,
|
||||
@@ -584,7 +584,7 @@ impl CaptureBackendTrait for OpenCvCaptureDevice {
|
||||
Ok(image_buf)
|
||||
}
|
||||
|
||||
fn frame_typed<F: PixelFormat>(
|
||||
fn frame_typed<F: FormatDecoder>(
|
||||
&mut self,
|
||||
) -> Result<ImageBuffer<crate::pixel_format::Output, Vec<u8>>, NokhwaError> {
|
||||
todo!()
|
||||
|
||||
@@ -18,7 +18,7 @@ use crate::{
|
||||
buffer::Buffer,
|
||||
error::NokhwaError,
|
||||
mjpeg_to_rgb,
|
||||
pixel_format::PixelFormat,
|
||||
pixel_format::FormatDecoder,
|
||||
utils::{CameraFormat, CameraInfo},
|
||||
yuyv422_to_rgb, ApiBackend, CameraControl, CaptureBackendTrait, ControlValueDescription,
|
||||
ControlValueSetter, FrameFormat, KnownCameraControl, KnownCameraControlFlag, Resolution,
|
||||
@@ -659,7 +659,7 @@ impl<'a> CaptureBackendTrait for V4LCaptureDevice<'a> {
|
||||
Ok(Buffer::new(cam_fmt.resolution(), conv, cam_fmt.format()))
|
||||
}
|
||||
|
||||
fn frame_typed<F: PixelFormat>(
|
||||
fn frame_typed<F: FormatDecoder>(
|
||||
&mut self,
|
||||
) -> Result<ImageBuffer<F::Output, Vec<u8>>, NokhwaError> {
|
||||
self.frame()?.to_image_with_custom_format::<F>()
|
||||
|
||||
+153
-48
@@ -14,77 +14,182 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use crate::pixel_format::PixelFormat;
|
||||
use crate::{mjpeg_to_rgb, yuyv422_to_rgb, FrameFormat, NokhwaError, Resolution};
|
||||
use crate::{
|
||||
mjpeg_to_rgb, pixel_format::FormatDecoder, yuyv422_to_rgb, FrameFormat, NokhwaError, Resolution,
|
||||
};
|
||||
use image::{ImageBuffer, Pixel};
|
||||
#[cfg(feature = "input-opencv")]
|
||||
use opencv::core::Mat;
|
||||
#[cfg(feature = "input-opencv")]
|
||||
use rgb::{FromSlice, RGB};
|
||||
use opencv::core::{Mat, Mat_AUTO_STEP, CV_8U, CV_8UC1, CV_8UC2, CV_8UC3, CV_8UC4};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// A buffer returned by a camera to accomodate custom decoding.
|
||||
/// Contains information of Resolution, the buffer's [`FrameFormat`], and the buffer.
|
||||
#[derive(Clone, Debug, Default, Hash, PartialOrd, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", Serialize, Deserialize)]
|
||||
pub struct Buffer {
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Buffer<'a> {
|
||||
resolution: Resolution,
|
||||
buffer: Vec<u8>,
|
||||
buffer: Cow<'a, [u8]>,
|
||||
source_frame_format: FrameFormat,
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
pub fn new(res: Resolution, buf: Vec<u8>, source_frame_format: FrameFormat) -> Self {
|
||||
impl<'a> Buffer<'a> {
|
||||
/// Creates a new buffer with a [`Vec`].
|
||||
pub fn new_with_vec(res: Resolution, buf: Vec<u8>, source_frame_format: FrameFormat) -> Self {
|
||||
Self {
|
||||
resolution: res,
|
||||
buffer: buf,
|
||||
buffer: Cow::Owned(buf),
|
||||
source_frame_format,
|
||||
}
|
||||
}
|
||||
/// Creates a new buffer with a [`&[u8]`].
|
||||
pub fn new_with_slice(res: Resolution, buf: &[u8], source_frame_format: FrameFormat) -> Self {
|
||||
Self {
|
||||
resolution: res,
|
||||
buffer: Cow::Borrowed(buf),
|
||||
source_frame_format,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_image<F: PixelFormat>(self) -> Result<ImageBuffer<F::Output, Vec<u8>>, NokhwaError> {
|
||||
let new_data = F::buffer_to_output(self.source_frame_format, &self.buffer)?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "input-opencv")]
|
||||
pub fn to_opencv_mat(self) -> Result<Mat, NokhwaError> {
|
||||
let buffer = match self.source_frame_format {
|
||||
FrameFormat::MJPEG => mjpeg_to_rgb(&self.buffer, use_alpha)?,
|
||||
FrameFormat::YUYV => yuyv422_to_rgb(&self.buffer, use_alpha)?,
|
||||
FrameFormat::GRAY8 => {
|
||||
if use_alpha {
|
||||
self.buffer.into_iter().flat_map(|x| [x, 255])
|
||||
} else {
|
||||
self.buffer
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(match self.source_frame_format {
|
||||
FrameFormat::MJPEG | FrameFormat::YUYV => Mat::from_slice_2d(
|
||||
buffer
|
||||
.as_rgb()
|
||||
.chunks(self.resolution.height_y as usize)
|
||||
.collect::<&[&[RGB<u8>]]>(),
|
||||
),
|
||||
FrameFormat::GRAY8 => Mat::from_slice_2d(
|
||||
buffer
|
||||
.chunks(self.resolution.height_y as usize)
|
||||
.collect::<&[&[u8]]>(),
|
||||
),
|
||||
}
|
||||
.map_err(|why| NokhwaError::ProcessFrameError {
|
||||
src: self.source_frame_format,
|
||||
destination: "OpenCV Mat".to_string(),
|
||||
error: why.to_string(),
|
||||
})?)
|
||||
}
|
||||
/// Get the [`Resolution`] of this buffer.
|
||||
pub fn resolution(&self) -> Resolution {
|
||||
self.resolution
|
||||
}
|
||||
|
||||
/// Get the data of this buffer.
|
||||
pub fn buffer(&self) -> &[u8] {
|
||||
&self.buffer
|
||||
}
|
||||
|
||||
/// Get a mutable reference to this buffer.
|
||||
///
|
||||
/// NOTE: Editing this may lead to decoding failure or unsafety!
|
||||
pub fn buffer_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.buffer
|
||||
}
|
||||
|
||||
/// Get the [`FrameFormat`] of this buffer.
|
||||
pub fn source_frame_format(&self) -> FrameFormat {
|
||||
self.source_frame_format
|
||||
}
|
||||
|
||||
/// Decodes a image with allocation using the provided [`FormatDecoder`].
|
||||
/// # Errors
|
||||
/// Will error when the decoding fails.
|
||||
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.buffer)?;
|
||||
let image =
|
||||
ImageBuffer::from_raw(self.resolution.width_x, self.resolution.height_y, new_data)
|
||||
.ok_or(Err(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.
|
||||
pub fn decode_image_to_buffer<F: FormatDecoder>(
|
||||
&self,
|
||||
buffer: &mut [u8],
|
||||
) -> Result<(), NokhwaError> {
|
||||
F::write_output_buffer(self.source_frame_format, &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_as_opencv_mat`](Self::decode_as_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.
|
||||
#[cfg(feature = "input-opencv")]
|
||||
#[cfg_attr(feature = "docs-features", doc(cfg(feature = "input-opencv")))]
|
||||
pub fn decode_opencv_mat<F: FormatDecoder>(&self) -> Result<Mat, NokhwaError> {
|
||||
let mut buffer = F::write_output(self.source_frame_format, &self.buffer)?;
|
||||
|
||||
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: self.source_frame_format,
|
||||
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,
|
||||
&mut buffer as *mut std::os::raw::c_void,
|
||||
Mat_AUTO_STEP,
|
||||
)
|
||||
.map_err(|why| NokhwaError::ProcessFrameError {
|
||||
src: self.source_frame_format,
|
||||
destination: "OpenCV Mat".to_string(),
|
||||
error: why.to_string(),
|
||||
})?;
|
||||
|
||||
Ok(mat1.clone().map_err(|why| NokhwaError::ProcessFrameError {
|
||||
src: self.source_frame_format,
|
||||
destination: "OpenCV Mat".to_string(),
|
||||
error: why.to_string(),
|
||||
})?)
|
||||
}
|
||||
}
|
||||
|
||||
/// Decodes a image with allocation using the provided [`FormatDecoder`] into a [`Mat`](https://docs.rs/opencv/latest/opencv/core/struct.Mat.html).
|
||||
/// # 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.
|
||||
///
|
||||
/// THIS WILL CAUSE UNSOUNDNESS IF YOU USE THE MAT WHILE THE BUFFER ITSELF IS DROPPED.
|
||||
#[cfg(feature = "input-opencv")]
|
||||
#[cfg_attr(feature = "docs-features", doc(cfg(feature = "input-opencv")))]
|
||||
pub unsafe fn decode_as_opencv_mat<F: FormatDecoder>(&mut self) -> Result<Mat, NokhwaError> {
|
||||
let resolution = self.resolution();
|
||||
let frame_format = self.source_frame_format();
|
||||
|
||||
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: frame_format,
|
||||
destination: "OpenCV Mat".to_string(),
|
||||
error: "Invalid Decoder FormatDecoder Channel Count".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
Ok(Mat::new_rows_cols_with_data(
|
||||
resolution.height_y as i32,
|
||||
resolution.width_x as i32,
|
||||
array_type,
|
||||
self.buffer_mut() as *mut std::os::raw::c_void,
|
||||
Mat_AUTO_STEP,
|
||||
)
|
||||
.map_err(|why| NokhwaError::ProcessFrameError {
|
||||
src: frame_format,
|
||||
destination: "OpenCV Mat".to_string(),
|
||||
error: why.to_string(),
|
||||
})?)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-5
@@ -16,7 +16,7 @@
|
||||
|
||||
use crate::{
|
||||
buffer::Buffer, ApiBackend, CameraControl, CameraFormat, CameraInfo, CaptureBackendTrait,
|
||||
ControlValueSetter, FrameFormat, KnownCameraControl, NokhwaError, Resolution,
|
||||
ControlValueSetter, FormatDecoder, FrameFormat, KnownCameraControl, NokhwaError, Resolution,
|
||||
};
|
||||
use std::{borrow::Cow, collections::HashMap};
|
||||
#[cfg(feature = "output-wgpu")]
|
||||
@@ -347,12 +347,11 @@ impl Camera {
|
||||
/// Directly writes the current frame(RGB24) into said `buffer`. If `convert_rgba` is true, the buffer written will be written as an RGBA frame instead of a RGB frame. Returns the amount of bytes written on successful capture.
|
||||
/// # Errors
|
||||
/// If the backend fails to get the frame (e.g. already taken, busy, doesn't exist anymore), or [`open_stream()`](CaptureBackendTrait::open_stream()) has not been called yet, this will error.
|
||||
pub fn write_frame_to_buffer(
|
||||
pub fn write_frame_to_buffer<F: FormatDecoder>(
|
||||
&mut self,
|
||||
buffer: &mut [u8],
|
||||
write_alpha: bool,
|
||||
) -> Result<usize, NokhwaError> {
|
||||
self.device.write_frame_to_buffer(buffer, write_alpha)
|
||||
let buffer = self.device.frame()?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "output-wgpu")]
|
||||
@@ -360,7 +359,7 @@ impl Camera {
|
||||
/// Directly copies a frame to a Wgpu texture. This will automatically convert the frame into a RGBA frame.
|
||||
/// # Errors
|
||||
/// If the frame cannot be captured or the resolution is 0 on any axis, this will error.
|
||||
pub fn frame_texture<'a, F: crate::PixelFormat>(
|
||||
pub fn frame_texture<'a, F: crate::FormatDecoder>(
|
||||
&mut self,
|
||||
device: &WgpuDevice,
|
||||
queue: &WgpuQueue,
|
||||
|
||||
+14
-27
@@ -14,12 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use crate::pixel_format::RgbAFormat;
|
||||
use crate::{
|
||||
error::NokhwaError,
|
||||
utils::{
|
||||
buf_mjpeg_to_rgb, buf_yuyv422_to_rgb, CameraFormat, CameraInfo, FrameFormat, Resolution,
|
||||
},
|
||||
Buffer, CameraControl, ControlValueSetter, KnownCameraControl, PixelFormat,
|
||||
Buffer, CameraControl, ControlValueSetter, FormatDecoder, KnownCameraControl,
|
||||
};
|
||||
use std::{borrow::Cow, collections::HashMap};
|
||||
#[cfg(feature = "output-wgpu")]
|
||||
@@ -151,19 +152,19 @@ pub trait CaptureBackendTrait {
|
||||
/// Checks if stream if open. If it is, it will return true.
|
||||
fn is_stream_open(&self) -> bool;
|
||||
|
||||
/// Will get a frame from the camera as a Raw RGB image buffer. Depending on the backend, if you have not called [`open_stream()`](CaptureBackendTrait::open_stream()) before you called this,
|
||||
/// Will get a frame from the camera as a [`Buffer`]. Depending on the backend, if you have not called [`open_stream()`](CaptureBackendTrait::open_stream()) before you called this,
|
||||
/// it will either return an error.
|
||||
/// # Errors
|
||||
/// If the backend fails to get the frame (e.g. already taken, busy, doesn't exist anymore), the decoding fails (e.g. MJPEG -> u8), or [`open_stream()`](CaptureBackendTrait::open_stream()) has not been called yet,
|
||||
/// this will error.
|
||||
fn frame(&mut self) -> Result<Buffer, NokhwaError>;
|
||||
fn frame<'a>(&mut self) -> Result<Buffer<'a>, NokhwaError>;
|
||||
|
||||
/// Will get a frame from the camera **without** any processing applied, meaning you will usually get a frame you need to decode yourself.
|
||||
/// # Errors
|
||||
/// If the backend fails to get the frame (e.g. already taken, busy, doesn't exist anymore), or [`open_stream()`](CaptureBackendTrait::open_stream()) has not been called yet, this will error.
|
||||
fn frame_raw(&mut self) -> Result<Cow<[u8]>, NokhwaError>;
|
||||
|
||||
/// The minimum buffer size needed to write the current frame. If `alpha` is true, it will instead return the minimum size of the RGBA buffer needed.
|
||||
/// The minimum buffer size needed to write the current frame. If `alpha` is true, it will instead return the minimum size of the buffer with an alpha channel as well.
|
||||
fn decoded_buffer_size(&self, alpha: bool) -> Result<usize, NokhwaError> {
|
||||
let cfmt = self.camera_format();
|
||||
let resolution = cfmt.resolution();
|
||||
@@ -177,34 +178,20 @@ pub trait CaptureBackendTrait {
|
||||
Ok((resolution.width() * resolution.height() * pxwidth) as usize)
|
||||
}
|
||||
|
||||
/// Directly writes the current frame(RGB24) into said `buffer`. If `convert_rgba` is true, the buffer written will be written as an RGBA frame instead of a RGB frame. Returns the amount of bytes written on successful capture.
|
||||
/// Directly writes the current frame into said `buffer` when provided a [`decoder`](crate::pixel_format::FormatDecoder).
|
||||
/// # Errors
|
||||
/// If the backend fails to get the frame (e.g. already taken, busy, doesn't exist anymore), or [`open_stream()`](CaptureBackendTrait::open_stream()) has not been called yet, this will error.
|
||||
fn write_frame_to_buffer(
|
||||
&mut self,
|
||||
buffer: &mut [u8],
|
||||
write_alpha: bool,
|
||||
decoder: &dyn FormatDecoder,
|
||||
) -> Result<usize, NokhwaError> {
|
||||
// FIXME: ??????
|
||||
let cfmt = self.camera_format();
|
||||
let frame = self.frame_raw()?;
|
||||
match cfmt.format() {
|
||||
FrameFormat::MJPEG => buf_mjpeg_to_rgb(&frame, buffer, write_alpha)?,
|
||||
FrameFormat::YUYV => buf_yuyv422_to_rgb(&frame, buffer, write_alpha)?,
|
||||
FrameFormat::GRAY8 => {
|
||||
let data = if write_alpha {
|
||||
frame
|
||||
.into_iter()
|
||||
.flat_map(|px| [*px, u8::MAX])
|
||||
.collect::<Cow<[u8]>>()
|
||||
} else {
|
||||
frame
|
||||
};
|
||||
let data = &self.frame_raw()?;
|
||||
let data_length = data.len();
|
||||
decoder.write_output_buffer(self.frame_format(), data, buffer)?;
|
||||
|
||||
buffer.copy_from_slice(&data)
|
||||
}
|
||||
};
|
||||
Ok(frame.len())
|
||||
Ok(data_length)
|
||||
}
|
||||
|
||||
#[cfg(feature = "output-wgpu")]
|
||||
@@ -218,9 +205,9 @@ pub trait CaptureBackendTrait {
|
||||
queue: &WgpuQueue,
|
||||
label: Option<&'a str>,
|
||||
) -> Result<WgpuTexture, NokhwaError> {
|
||||
use image::RgbaImage;
|
||||
use crate::pixel_format::RgbAFormat;
|
||||
use std::{convert::TryFrom, num::NonZeroU32};
|
||||
let frame = self.frame()?.to_image();
|
||||
let frame = self.frame()?.decode_image::<RgbAFormat>()?;
|
||||
|
||||
let texture_size = Extent3d {
|
||||
width: frame.width(),
|
||||
@@ -255,7 +242,7 @@ pub trait CaptureBackendTrait {
|
||||
origin: wgpu::Origin3d::ZERO,
|
||||
aspect: TextureAspect::All,
|
||||
},
|
||||
&rgba_frame.to_vec(),
|
||||
&frame.to_vec(),
|
||||
ImageDataLayout {
|
||||
offset: 0,
|
||||
bytes_per_row: width_nonzero,
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ pub mod js_camera;
|
||||
#[cfg_attr(feature = "docs-features", doc(cfg(feature = "input-ipcam")))]
|
||||
pub mod network_camera;
|
||||
mod pixel_format;
|
||||
pub use pixel_format::PixelFormat;
|
||||
pub use pixel_format::FormatDecoder;
|
||||
mod query;
|
||||
/// A camera that runs in a different thread and can call your code based on callbacks.
|
||||
#[cfg(feature = "output-threaded")]
|
||||
|
||||
+149
-10
@@ -22,11 +22,18 @@ use image::{Luma, LumaA, Primitive};
|
||||
use image::{Pixel, Rgb, Rgba};
|
||||
use std::{fmt::Debug, hash::Hash};
|
||||
|
||||
pub trait PixelFormat: Copy + Clone + Debug + Default + Hash + Send + Sync {
|
||||
/// Trait that has methods to convert raw data from the webcam to a proper raw image.
|
||||
pub trait FormatDecoder: Copy + Clone + Debug + Default + Hash + Send + Sync {
|
||||
type Output: Pixel;
|
||||
|
||||
/// 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, data: &[u8]) -> Result<Vec<u8>, NokhwaError>;
|
||||
|
||||
/// 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,
|
||||
data: &[u8],
|
||||
@@ -34,10 +41,16 @@ pub trait PixelFormat: Copy + Clone + Debug + Default + Hash + Send + Sync {
|
||||
) -> Result<(), NokhwaError>;
|
||||
}
|
||||
|
||||
/// A Zero-Size-Type that contains the definition to convert a given image stream to an RGB888 in the [`Buffer`](crate::buffer::Buffer)'s [`.to_image()`](crate::buffer::Buffer::to_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 PixelFormat for RgbFormat {
|
||||
impl FormatDecoder for RgbFormat {
|
||||
type Output = Rgb<u8>;
|
||||
|
||||
fn write_output(fcc: FrameFormat, data: &[u8]) -> Result<Vec<u8>, NokhwaError> {
|
||||
@@ -62,18 +75,39 @@ impl PixelFormat for RgbFormat {
|
||||
match fcc {
|
||||
FrameFormat::MJPEG => buf_mjpeg_to_rgb(data, dest, false),
|
||||
FrameFormat::YUYV => buf_yuyv422_to_rgb(data, dest, false),
|
||||
FrameFormat::GRAY8 => {}
|
||||
FrameFormat::GRAY8 => {
|
||||
if dest.len() != data * 3 {
|
||||
return Err(NokhwaError::ProcessFrameError {
|
||||
src: fcc,
|
||||
destination: "Luma => RGB".to_string(),
|
||||
error: "Bad buffer length".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
data.into_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;
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A Zero-Size-Type that contains the definition to convert a given image stream to an RGBA8888 in the [`Buffer`](crate::buffer::Buffer)'s [`.to_image()`](crate::buffer::Buffer::to_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;
|
||||
pub struct RgbAFormat;
|
||||
|
||||
impl PixelFormat for RgbaFormat {
|
||||
impl FormatDecoder for RgbAFormat {
|
||||
type Output = Rgba<u8>;
|
||||
|
||||
fn buffer_to_output(fcc: FrameFormat, data: &[u8]) -> Result<Vec<u8>, NokhwaError> {
|
||||
fn write_output(fcc: FrameFormat, data: &[u8]) -> Result<Vec<u8>, NokhwaError> {
|
||||
match fcc {
|
||||
FrameFormat::MJPEG => mjpeg_to_rgb(data, true),
|
||||
FrameFormat::YUYV => yuyv422_to_rgb(data, true),
|
||||
@@ -86,15 +120,49 @@ impl PixelFormat for RgbaFormat {
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_output_buffer(
|
||||
fcc: FrameFormat,
|
||||
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::GRAY8 => {
|
||||
if dest.len() != data * 4 {
|
||||
return Err(NokhwaError::ProcessFrameError {
|
||||
src: fcc,
|
||||
destination: "Luma => RGBA".to_string(),
|
||||
error: "Bad buffer length".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
data.into_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;
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 [`.to_image()`](crate::buffer::Buffer::to_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 PixelFormat for LumaFormat {
|
||||
impl FormatDecoder for LumaFormat {
|
||||
type Output = Luma<u8>;
|
||||
|
||||
fn buffer_to_output(fcc: FrameFormat, data: &[u8]) -> Result<Vec<u8>, NokhwaError> {
|
||||
fn write_output(fcc: FrameFormat, data: &[u8]) -> Result<Vec<u8>, NokhwaError> {
|
||||
match fcc {
|
||||
FrameFormat::MJPEG => Ok(mjpeg_to_rgb(data, false)?
|
||||
.as_slice()
|
||||
@@ -117,15 +185,46 @@ impl PixelFormat for LumaFormat {
|
||||
FrameFormat::GRAY8 => data.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_output_buffer(
|
||||
fcc: FrameFormat,
|
||||
data: &[u8],
|
||||
dest: &mut [u8],
|
||||
) -> Result<(), NokhwaError> {
|
||||
match fcc {
|
||||
FrameFormat::MJPEG => {
|
||||
// FIXME: implement!
|
||||
Err(NokhwaError::ProcessFrameError {
|
||||
src: fcc,
|
||||
destination: "Luma => RGB".to_string(),
|
||||
error: "Conversion Error".to_string(),
|
||||
})
|
||||
}
|
||||
FrameFormat::YUYV => Err(NokhwaError::ProcessFrameError {
|
||||
src: fcc,
|
||||
destination: "Luma => RGB".to_string(),
|
||||
error: "Conversion Error".to_string(),
|
||||
}),
|
||||
FrameFormat::GRAY8 => data.into_iter().zip(dest.iter_mut()).for_each(|(pxv, d)| {
|
||||
*d = *pxv;
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 [`.to_image()`](crate::buffer::Buffer::to_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 PixelFormat for LumaAFormat {
|
||||
impl FormatDecoder for LumaAFormat {
|
||||
type Output = LumaA<u8>;
|
||||
|
||||
fn buffer_to_output(fcc: FrameFormat, data: &[u8]) -> Result<Vec<u8>, NokhwaError> {
|
||||
fn write_output(fcc: FrameFormat, data: &[u8]) -> Result<Vec<u8>, NokhwaError> {
|
||||
match fcc {
|
||||
FrameFormat::MJPEG => Ok(mjpeg_to_rgb(data, false)?
|
||||
.as_slice()
|
||||
@@ -148,4 +247,44 @@ impl PixelFormat for LumaAFormat {
|
||||
FrameFormat::GRAY8 => data.into_iter().flat_map(|x| [*x, 255]).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_output_buffer(
|
||||
fcc: FrameFormat,
|
||||
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::GRAY8 => {
|
||||
if dest.len() != data.len() * 2 {
|
||||
return Err(NokhwaError::ProcessFrameError {
|
||||
src: fcc,
|
||||
destination: "GRAY8 => LumaA".to_string(),
|
||||
error: "Conversion Error".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
data.into_iter()
|
||||
.zip(dest.chunks_exact_mut(2))
|
||||
.enumerate()
|
||||
.for_each(|(idx, (pxv, d))| {
|
||||
let index = idx * 2;
|
||||
*d[index] = pxv;
|
||||
*d[index + 1] = 255;
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use crate::{pixel_format::PixelFormat, NokhwaError};
|
||||
use crate::{pixel_format::FormatDecoder, NokhwaError};
|
||||
#[cfg(any(
|
||||
all(
|
||||
feature = "input-avfoundation",
|
||||
@@ -82,7 +82,7 @@ impl Display for FrameFormat {
|
||||
|
||||
impl<P> From<P> for FrameFormat
|
||||
where
|
||||
P: PixelFormat,
|
||||
P: FormatDecoder,
|
||||
{
|
||||
fn from(_: P) -> Self {
|
||||
P::CODE
|
||||
|
||||
Reference in New Issue
Block a user