#[cfg(feature = "parallel")]
use rayon::prelude::*;
#[cfg(feature = "simd")]
use crate::core::simd::{
simd_bgr_to_gray_u8, simd_bgra_to_gray_u8, simd_rgb_to_gray_u8, simd_rgba_to_gray_u8,
};
use crate::core::Matrix;
#[inline(always)]
fn rgb_to_gray_row(out_row: &mut [u8], in_row: &[u8]) {
#[cfg(feature = "simd")]
{
simd_rgb_to_gray_u8(out_row, in_row);
}
#[cfg(not(feature = "simd"))]
{
for (out_pixel, in_val) in out_row.iter_mut().zip(in_row.chunks_exact(3)) {
let r = in_val[0] as f32;
let g = in_val[1] as f32;
let b = in_val[2] as f32;
*out_pixel = (0.299 * r + 0.587 * g + 0.114 * b).round() as u8;
}
}
}
#[inline(always)]
fn bgr_to_gray_row(out_row: &mut [u8], in_row: &[u8]) {
#[cfg(feature = "simd")]
{
simd_bgr_to_gray_u8(out_row, in_row);
}
#[cfg(not(feature = "simd"))]
{
for (out_pixel, in_val) in out_row.iter_mut().zip(in_row.chunks_exact(3)) {
let b = in_val[0] as f32;
let g = in_val[1] as f32;
let r = in_val[2] as f32;
*out_pixel = (0.299 * r + 0.587 * g + 0.114 * b).round() as u8;
}
}
}
#[inline(always)]
fn rgba_to_gray_row(out_row: &mut [u8], in_row: &[u8]) {
#[cfg(feature = "simd")]
{
simd_rgba_to_gray_u8(out_row, in_row);
}
#[cfg(not(feature = "simd"))]
{
for (out_pixel, in_val) in out_row.iter_mut().zip(in_row.chunks_exact(4)) {
let r = in_val[0] as f32;
let g = in_val[1] as f32;
let b = in_val[2] as f32;
*out_pixel = (0.299 * r + 0.587 * g + 0.114 * b).round() as u8;
}
}
}
#[inline(always)]
fn bgra_to_gray_row(out_row: &mut [u8], in_row: &[u8]) {
#[cfg(feature = "simd")]
{
simd_bgra_to_gray_u8(out_row, in_row);
}
#[cfg(not(feature = "simd"))]
{
for (out_pixel, in_val) in out_row.iter_mut().zip(in_row.chunks_exact(4)) {
let b = in_val[0] as f32;
let g = in_val[1] as f32;
let r = in_val[2] as f32;
*out_pixel = (0.299 * r + 0.587 * g + 0.114 * b).round() as u8;
}
}
}
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ColorConversionCode {
COLOR_BGR2GRAY,
COLOR_RGB2GRAY,
COLOR_BGRA2GRAY,
COLOR_RGBA2GRAY,
COLOR_GRAY2RGB,
COLOR_GRAY2BGR,
COLOR_GRAY2RGBA,
COLOR_GRAY2BGRA,
}
pub fn cvt_color(src: &Matrix<u8>, code: ColorConversionCode) -> Result<Matrix<u8>, &'static str> {
match code {
ColorConversionCode::COLOR_RGB2GRAY => cvt_color_rgb_to_gray(src),
ColorConversionCode::COLOR_BGR2GRAY => cvt_color_bgr_to_gray(src),
ColorConversionCode::COLOR_RGBA2GRAY => cvt_color_rgba_to_gray(src),
ColorConversionCode::COLOR_BGRA2GRAY => cvt_color_bgra_to_gray(src),
ColorConversionCode::COLOR_GRAY2RGB => cvt_color_gray_to_rgb(src),
ColorConversionCode::COLOR_GRAY2BGR => cvt_color_gray_to_bgr(src),
ColorConversionCode::COLOR_GRAY2RGBA => cvt_color_gray_to_rgba(src),
ColorConversionCode::COLOR_GRAY2BGRA => cvt_color_gray_to_bgra(src),
}
}
pub fn cvt_color_rgb_to_gray(input: &Matrix<u8>) -> Result<Matrix<u8>, &'static str> {
if input.channels != 3 {
return Err("Input matrix must have exactly 3 channels");
}
let mut output = Matrix::<u8>::new(input.rows, input.cols, 1);
let out_row_len = output.cols;
let in_row_len = input.cols * 3;
#[cfg(feature = "parallel")]
{
output
.data
.par_chunks_exact_mut(out_row_len)
.zip(input.data.par_chunks_exact(in_row_len))
.for_each(|(out_row, in_row)| {
rgb_to_gray_row(out_row, in_row);
});
}
#[cfg(not(feature = "parallel"))]
{
output
.data
.chunks_exact_mut(out_row_len)
.zip(input.data.chunks_exact(in_row_len))
.for_each(|(out_row, in_row)| {
rgb_to_gray_row(out_row, in_row);
});
}
Ok(output)
}
pub fn cvt_color_bgr_to_gray(input: &Matrix<u8>) -> Result<Matrix<u8>, &'static str> {
if input.channels != 3 {
return Err("Input matrix must have exactly 3 channels");
}
let mut output = Matrix::<u8>::new(input.rows, input.cols, 1);
let out_row_len = output.cols;
let in_row_len = input.cols * 3;
#[cfg(feature = "parallel")]
{
output
.data
.par_chunks_exact_mut(out_row_len)
.zip(input.data.par_chunks_exact(in_row_len))
.for_each(|(out_row, in_row)| {
bgr_to_gray_row(out_row, in_row);
});
}
#[cfg(not(feature = "parallel"))]
{
output
.data
.chunks_exact_mut(out_row_len)
.zip(input.data.chunks_exact(in_row_len))
.for_each(|(out_row, in_row)| {
bgr_to_gray_row(out_row, in_row);
});
}
Ok(output)
}
pub fn cvt_color_rgba_to_gray(input: &Matrix<u8>) -> Result<Matrix<u8>, &'static str> {
if input.channels != 4 {
return Err("Input matrix must have exactly 4 channels");
}
let mut output = Matrix::<u8>::new(input.rows, input.cols, 1);
let out_row_len = output.cols;
let in_row_len = input.cols * 4;
#[cfg(feature = "parallel")]
{
output
.data
.par_chunks_exact_mut(out_row_len)
.zip(input.data.par_chunks_exact(in_row_len))
.for_each(|(out_row, in_row)| {
rgba_to_gray_row(out_row, in_row);
});
}
#[cfg(not(feature = "parallel"))]
{
output
.data
.chunks_exact_mut(out_row_len)
.zip(input.data.chunks_exact(in_row_len))
.for_each(|(out_row, in_row)| {
rgba_to_gray_row(out_row, in_row);
});
}
Ok(output)
}
pub fn cvt_color_bgra_to_gray(input: &Matrix<u8>) -> Result<Matrix<u8>, &'static str> {
if input.channels != 4 {
return Err("Input matrix must have exactly 4 channels");
}
let mut output = Matrix::<u8>::new(input.rows, input.cols, 1);
let out_row_len = output.cols;
let in_row_len = input.cols * 4;
#[cfg(feature = "parallel")]
{
output
.data
.par_chunks_exact_mut(out_row_len)
.zip(input.data.par_chunks_exact(in_row_len))
.for_each(|(out_row, in_row)| {
bgra_to_gray_row(out_row, in_row);
});
}
#[cfg(not(feature = "parallel"))]
{
output
.data
.chunks_exact_mut(out_row_len)
.zip(input.data.chunks_exact(in_row_len))
.for_each(|(out_row, in_row)| {
bgra_to_gray_row(out_row, in_row);
});
}
Ok(output)
}
pub fn cvt_color_gray_to_rgb(input: &Matrix<u8>) -> Result<Matrix<u8>, &'static str> {
if input.channels != 1 {
return Err("Input matrix must have exactly 1 channels");
}
let mut output = Matrix::<u8>::new(input.rows, input.cols, 3);
let out_row_len = output.cols * 3;
let in_row_len = input.cols;
#[cfg(feature = "parallel")]
{
output
.data
.par_chunks_exact_mut(out_row_len)
.zip(input.data.par_chunks_exact(in_row_len))
.for_each(|(out_row, in_row)| {
for (out_val, in_pixel) in out_row.chunks_exact_mut(3).zip(in_row.iter()) {
let v = *in_pixel;
out_val[0] = v;
out_val[1] = v;
out_val[2] = v;
}
});
}
#[cfg(not(feature = "parallel"))]
{
output
.data
.chunks_exact_mut(out_row_len)
.zip(input.data.chunks_exact(in_row_len))
.for_each(|(out_row, in_row)| {
for (out_val, in_pixel) in out_row.chunks_exact_mut(3).zip(in_row.iter()) {
let v = *in_pixel;
out_val[0] = v;
out_val[1] = v;
out_val[2] = v;
}
});
}
Ok(output)
}
pub fn cvt_color_gray_to_bgr(input: &Matrix<u8>) -> Result<Matrix<u8>, &'static str> {
if input.channels != 1 {
return Err("Input matrix must have exactly 1 channels");
}
let mut output = Matrix::<u8>::new(input.rows, input.cols, 3);
let out_row_len = output.cols * 3;
let in_row_len = input.cols;
#[cfg(feature = "parallel")]
{
output
.data
.par_chunks_exact_mut(out_row_len)
.zip(input.data.par_chunks_exact(in_row_len))
.for_each(|(out_row, in_row)| {
for (out_val, in_pixel) in out_row.chunks_exact_mut(3).zip(in_row.iter()) {
let v = *in_pixel;
out_val[0] = v;
out_val[1] = v;
out_val[2] = v;
}
});
}
#[cfg(not(feature = "parallel"))]
{
output
.data
.chunks_exact_mut(out_row_len)
.zip(input.data.chunks_exact(in_row_len))
.for_each(|(out_row, in_row)| {
for (out_val, in_pixel) in out_row.chunks_exact_mut(3).zip(in_row.iter()) {
let v = *in_pixel;
out_val[0] = v;
out_val[1] = v;
out_val[2] = v;
}
});
}
Ok(output)
}
pub fn cvt_color_gray_to_rgba(input: &Matrix<u8>) -> Result<Matrix<u8>, &'static str> {
if input.channels != 1 {
return Err("Input matrix must have exactly 1 channels");
}
let mut output = Matrix::<u8>::new(input.rows, input.cols, 4);
let out_row_len = output.cols * 4;
let in_row_len = input.cols;
#[cfg(feature = "parallel")]
{
output
.data
.par_chunks_exact_mut(out_row_len)
.zip(input.data.par_chunks_exact(in_row_len))
.for_each(|(out_row, in_row)| {
for (out_val, in_pixel) in out_row.chunks_exact_mut(4).zip(in_row.iter()) {
let v = *in_pixel;
out_val[0] = v;
out_val[1] = v;
out_val[2] = v;
out_val[3] = 255;
}
});
}
#[cfg(not(feature = "parallel"))]
{
output
.data
.chunks_exact_mut(out_row_len)
.zip(input.data.chunks_exact(in_row_len))
.for_each(|(out_row, in_row)| {
for (out_val, in_pixel) in out_row.chunks_exact_mut(4).zip(in_row.iter()) {
let v = *in_pixel;
out_val[0] = v;
out_val[1] = v;
out_val[2] = v;
out_val[3] = 255;
}
});
}
Ok(output)
}
pub fn cvt_color_gray_to_bgra(input: &Matrix<u8>) -> Result<Matrix<u8>, &'static str> {
if input.channels != 1 {
return Err("Input matrix must have exactly 1 channels");
}
let mut output = Matrix::<u8>::new(input.rows, input.cols, 4);
let out_row_len = output.cols * 4;
let in_row_len = input.cols;
#[cfg(feature = "parallel")]
{
output
.data
.par_chunks_exact_mut(out_row_len)
.zip(input.data.par_chunks_exact(in_row_len))
.for_each(|(out_row, in_row)| {
for (out_val, in_pixel) in out_row.chunks_exact_mut(4).zip(in_row.iter()) {
let v = *in_pixel;
out_val[0] = v;
out_val[1] = v;
out_val[2] = v;
out_val[3] = 255;
}
});
}
#[cfg(not(feature = "parallel"))]
{
output
.data
.chunks_exact_mut(out_row_len)
.zip(input.data.chunks_exact(in_row_len))
.for_each(|(out_row, in_row)| {
for (out_val, in_pixel) in out_row.chunks_exact_mut(4).zip(in_row.iter()) {
let v = *in_pixel;
out_val[0] = v;
out_val[1] = v;
out_val[2] = v;
out_val[3] = 255;
}
});
}
Ok(output)
}