Open a terminal inside the library root directory.
To build for debug experience:
cargo build
To build an optimized library:
cargo build --release
Run unit tests:
cargo test
Run benchmark:
cargo bench
Advanced benchmark mode.
There are two benchmark scripts:
run-bench.ps1 for Windows
run-bench.sh for Linux and MacOS
They allow to obtain more stable results than cargo bench, by reducing variance due to:
CPU migration
File system caching
Process priority
Moreover, the Linux script support hardware performance counters, e.g. it is possible to output
consumed CPU cycles instead of elapsed time.
Linux examples:
./run-bench -c 1 # runs cargo bench and outputs CPU cycles
./run.bench -c 1 -p "/i420" # runs cargo bench, output CPU cycles, filtering tests that contains '/i420'
WebAssembly
Install the needed dependencies:
rustup target add wasm32-unknown-unknown
To build for debug experience:
cargo build --target wasm32-unknown-unknown
To test, ensure you have installed wasm-pack. Then:
wasm-pack test --node
no_std support
The library supports no_std environments, making it suitable for embedded systems and other resource-constrained platforms. By default, the library is built with standard library support, but you can disable it by opting out of the default features:
In no_std mode, the library maintains full functionality for image color conversions while avoiding heap allocations and standard library dependencies.
When building without std support, you must use panic = "abort". Add this to your Cargo.toml:
Note: Depending on your target and configuration, you may also need to provide a rust_eh_personality lang item.
Usage
Image conversion
Convert an image from bgra to nv12 (two planes) format containing yuv in BT601:
use dcv_color_primitives as dcp;
use dcp::{convert_image, ColorSpace, ImageFormat, PixelFormat};
fn main() {
const WIDTH: u32 = 640;
const HEIGHT: u32 = 480;
let src_data = vec![0u8; 4 * (WIDTH as usize) * (HEIGHT as usize)];
let mut y_data = vec![0u8; (WIDTH as usize) * (HEIGHT as usize)];
let mut uv_data = vec![0u8; (WIDTH as usize) * (HEIGHT as usize) / 2];
let src_format = ImageFormat {
pixel_format: PixelFormat::Bgra,
color_space: ColorSpace::Rgb,
num_planes: 1,
};
let dst_format = ImageFormat {
pixel_format: PixelFormat::Nv12,
color_space: ColorSpace::Bt601,
num_planes: 2,
};
convert_image(
WIDTH,
HEIGHT,
&src_format,
None,
&[&src_data],
&dst_format,
None,
&mut [&mut y_data, &mut uv_data],
);
}
Error Handling
The library functions return a Result describing the operation outcome:
Result
Description
Ok(())
The operation succeeded
Err(ErrorKind::InvalidValue)
One or more parameters have invalid values for the called function
Err(ErrorKind::InvalidOperation)
The combination of parameters is unsupported for the called function
Err(ErrorKind::NotEnoughData)
One or more buffers are not correctly sized
In the following example, result will match Err(ErrorKind::InvalidValue), because ColorSpace::Bt709
color space is not compatible with PixelFormat::Bgra:
use dcv_color_primitives as dcp;
use dcp::{convert_image, ColorSpace, ErrorKind, ImageFormat, PixelFormat};
fn main() {
const WIDTH: u32 = 640;
const HEIGHT: u32 = 480;
let src_data = vec![0u8; 4 * (WIDTH as usize) * (HEIGHT as usize)];
let mut y_data = vec![0u8; (WIDTH as usize) * (HEIGHT as usize)];
let mut uv_data = vec![0u8; (WIDTH as usize) * (HEIGHT as usize) / 2];
let src_format = ImageFormat {
pixel_format: PixelFormat::Bgra,
color_space: ColorSpace::Bt709, // Invalid: RGB format with YUV color space
num_planes: 1,
};
let dst_format = ImageFormat {
pixel_format: PixelFormat::Nv12,
color_space: ColorSpace::Bt601,
num_planes: 2,
};
let status = convert_image(
WIDTH,
HEIGHT,
&src_format,
None,
&[&src_data],
&dst_format,
None,
&mut [&mut y_data, &mut uv_data],
);
match status {
Err(ErrorKind::InvalidValue) => (),
_ => panic!("Expected ErrorKind::InvalidValue"),
}
}
Even better, you might want to propagate errors to the caller function or mix with some other error types:
use dcv_color_primitives as dcp;
use dcp::{convert_image, ColorSpace, ErrorKind, ImageFormat, PixelFormat};
fn main() -> Result<(), ErrorKind> {
const WIDTH: u32 = 640;
const HEIGHT: u32 = 480;
let src_data = vec![0u8; 4 * (WIDTH as usize) * (HEIGHT as usize)];
let mut y_data = vec![0u8; (WIDTH as usize) * (HEIGHT as usize)];
let mut uv_data = vec![0u8; (WIDTH as usize) * (HEIGHT as usize) / 2];
let src_format = ImageFormat {
pixel_format: PixelFormat::Bgra,
color_space: ColorSpace::Bt709, // Invalid: RGB format with YUV color space
num_planes: 1,
};
let dst_format = ImageFormat {
pixel_format: PixelFormat::Nv12,
color_space: ColorSpace::Bt601,
num_planes: 2,
};
convert_image(
WIDTH,
HEIGHT,
&src_format,
None,
&[&src_data],
&dst_format,
None,
&mut [&mut y_data, &mut uv_data],
)?;
Ok(())
}
Buffer size computation
So far, buffers were sized taking into account the image pixel format and dimensions; However,
you can use a function to compute how many bytes are needed to store an image of a given format
and size:
use dcv_color_primitives as dcp;
use dcp::{get_buffers_size, ColorSpace, ErrorKind, ImageFormat, PixelFormat};
fn main() -> Result<(), ErrorKind> {
const WIDTH: u32 = 640;
const HEIGHT: u32 = 480;
const NUM_PLANES: u32 = 1;
let format = ImageFormat {
pixel_format: PixelFormat::Bgra,
color_space: ColorSpace::Rgb,
num_planes: NUM_PLANES,
};
let mut sizes = [0usize; NUM_PLANES as usize];
get_buffers_size(WIDTH, HEIGHT, &format, None, &mut sizes)?;
let buffer = vec![0u8; sizes[0]];
// Do something with buffer
// --snip--
Ok(())
}
Image planes
If your data is scattered in multiple buffers that are not necessarily contiguous, you can provide image planes:
use dcv_color_primitives as dcp;
use dcp::{convert_image, get_buffers_size, ColorSpace, ErrorKind, ImageFormat, PixelFormat};
fn main() -> Result<(), ErrorKind> {
const WIDTH: u32 = 640;
const HEIGHT: u32 = 480;
const NUM_SRC_PLANES: u32 = 2;
const NUM_DST_PLANES: u32 = 1;
let src_format = ImageFormat {
pixel_format: PixelFormat::Nv12,
color_space: ColorSpace::Bt709,
num_planes: NUM_SRC_PLANES,
};
let mut src_sizes = [0usize; NUM_SRC_PLANES as usize];
get_buffers_size(WIDTH, HEIGHT, &src_format, None, &mut src_sizes)?;
let src_y = vec![0u8; src_sizes[0]];
let src_uv = vec![0u8; src_sizes[1]];
let dst_format = ImageFormat {
pixel_format: PixelFormat::Bgra,
color_space: ColorSpace::Rgb,
num_planes: NUM_DST_PLANES,
};
let mut dst_sizes = [0usize; NUM_DST_PLANES as usize];
get_buffers_size(WIDTH, HEIGHT, &dst_format, None, &mut dst_sizes)?;
let mut dst_data = vec![0u8; dst_sizes[0]];
convert_image(
WIDTH,
HEIGHT,
&src_format,
None,
&[&src_y, &src_uv],
&dst_format,
None,
&mut [&mut dst_data],
)?;
Ok(())
}
Stride support
To take into account data which is not tightly packed, you can provide image strides:
use dcv_color_primitives as dcp;
use dcp::{convert_image, get_buffers_size, ColorSpace, ErrorKind, ImageFormat, PixelFormat};
fn main() -> Result<(), ErrorKind> {
const WIDTH: u32 = 640;
const HEIGHT: u32 = 480;
const NUM_SRC_PLANES: u32 = 1;
const NUM_DST_PLANES: u32 = 2;
const RGB_STRIDE: usize = 4 * (3 * (WIDTH as usize)).div_ceil(4);
let src_format = ImageFormat {
pixel_format: PixelFormat::Bgr,
color_space: ColorSpace::Rgb,
num_planes: NUM_SRC_PLANES,
};
let src_strides = [RGB_STRIDE];
let mut src_sizes = [0usize; NUM_SRC_PLANES as usize];
get_buffers_size(WIDTH, HEIGHT, &src_format, Some(&src_strides), &mut src_sizes)?;
let src_data = vec![0u8; src_sizes[0]];
let dst_format = ImageFormat {
pixel_format: PixelFormat::Nv12,
color_space: ColorSpace::Bt709,
num_planes: NUM_DST_PLANES,
};
let mut dst_sizes = [0usize; NUM_DST_PLANES as usize];
get_buffers_size(WIDTH, HEIGHT, &dst_format, None, &mut dst_sizes)?;
let mut dst_y = vec![0u8; dst_sizes[0]];
let mut dst_uv = vec![0u8; dst_sizes[1]];
convert_image(
WIDTH,
HEIGHT,
&src_format,
Some(&src_strides),
&[&src_data],
&dst_format,
None,
&mut [&mut dst_y, &mut dst_uv],
)?;
Ok(())
}
See documentation for further information.
C bindings
DCV Color Primitives provides C bindings. A static library will be automatically generated for the
default build.
In order to include DCV Color Primitives inside your application library, you need to statically link to dcv_color_primitives
The API is slightly different than the rust one. Check dcv_color_primitives.h for examples and further information.
A meson build system is provided in order to build the static library and install it together
with include file and a pkgconfig file. There are also some unit tests written in C,
to add some coverage also for the bindings. Minimal instructions are provided below, refer to meson’s
help for further instructions:
Windows
Visual Studio is required. At least the following packages are required:
MSBuild
MSVC - C++ build tools
Windows 10 SDK
Install meson, you can choose one of the following methods:
All build commands have to be issued from Native Tools Command Prompt for VS (x86 or x64 depending on what platform you want to build)
Linux
The following example is for Ubuntu:
#install python3
apt install python3
#install meson. See https://mesonbuild.com/Getting-meson.html for details or if you want to install through pip.
apt install meson
#install ninja
apt install ninja-build
DCV Color Primitives - dcp
DCV Color Primitives is a library to perform image color model conversion.
Design guidelines
[*]: Supplemental cpu extension sets not yet supported. [**]: Neon cpu extension sets supported for rgb and rgb to yuv conversions
Image format conversion
The library is currenty able to convert the following pixel formats:
Color models
The supported color models are:
Both standard range (0-235) and full range (0-255) are supported.
Requirements
Windows
Linux
You may require administrative privileges.
Building
Open a terminal inside the library root directory.
To build for debug experience:
To build an optimized library:
Run unit tests:
Run benchmark:
Advanced benchmark mode. There are two benchmark scripts:
run-bench.ps1for Windowsrun-bench.shfor Linux and MacOSThey allow to obtain more stable results than
cargo bench, by reducing variance due to:Moreover, the Linux script support hardware performance counters, e.g. it is possible to output consumed CPU cycles instead of elapsed time.
Linux examples:
WebAssembly
Install the needed dependencies:
To build for debug experience:
To test, ensure you have installed wasm-pack. Then:
no_std support
The library supports
no_stdenvironments, making it suitable for embedded systems and other resource-constrained platforms. By default, the library is built with standard library support, but you can disable it by opting out of the default features:In
no_stdmode, the library maintains full functionality for image color conversions while avoiding heap allocations and standard library dependencies.When building without std support, you must use
panic = "abort". Add this to your Cargo.toml:Additionally, you must provide a panic handler in your application:
Note: Depending on your target and configuration, you may also need to provide a
rust_eh_personalitylang item.Usage
Image conversion
Convert an image from bgra to nv12 (two planes) format containing yuv in BT601:
Error Handling
The library functions return a
Resultdescribing the operation outcome:Ok(())Err(ErrorKind::InvalidValue)Err(ErrorKind::InvalidOperation)Err(ErrorKind::NotEnoughData)In the following example,
resultwill matchErr(ErrorKind::InvalidValue), becauseColorSpace::Bt709color space is not compatible withPixelFormat::Bgra:Even better, you might want to propagate errors to the caller function or mix with some other error types:
Buffer size computation
So far, buffers were sized taking into account the image pixel format and dimensions; However, you can use a function to compute how many bytes are needed to store an image of a given format and size:
Image planes
If your data is scattered in multiple buffers that are not necessarily contiguous, you can provide image planes:
Stride support
To take into account data which is not tightly packed, you can provide image strides:
See documentation for further information.
C bindings
DCV Color Primitives provides C bindings. A static library will be automatically generated for the default build.
In order to include DCV Color Primitives inside your application library, you need to statically link to dcv_color_primitives
The API is slightly different than the rust one. Check dcv_color_primitives.h for examples and further information.
A meson build system is provided in order to build the static library and install it together with include file and a pkgconfig file. There are also some unit tests written in C, to add some coverage also for the bindings. Minimal instructions are provided below, refer to meson’s help for further instructions:
Windows Visual Studio is required. At least the following packages are required:
Install meson, you can choose one of the following methods:
Note: Minimum required meson version is 1.0.0.
All build commands have to be issued from Native Tools Command Prompt for VS (x86 or x64 depending on what platform you want to build)
Linux The following example is for Ubuntu:
You may require administrative privileges.
Build Move inside the library root directory:
Then:
Run the tests
A timeout scale factor of 10 is required because some tests take longer than default 30 seconds to complete.
Install
License
This library is licensed under the MIT-0 License. See the LICENSE file.