use std::cmp;
use crate::enums::White;
use crate::image::Image;
use crate::util;
use crate::util::constants::{GAMMA, SRGB_TO_XYZ_MAT, XYZ_TO_SRGB_MAT};
#[cfg(feature = "simd")]
use crate::simd;
pub fn rgb_to_grayscale(input: &Image<u8>) -> Image<u8> {
#[cfg(feature = "simd")]
{
if is_x86_feature_detected!("avx2") {
simd::avg_checked_256_u8(input)
} else {
rgb_to_grayscale_norm(input)
}
}
#[cfg(not(feature = "simd"))]
rgb_to_grayscale_norm(input)
}
fn rgb_to_grayscale_norm(input: &Image<u8>) -> Image<u8> {
input.map_pixels_if_alpha(|channels, p_out| {
let mut sum = 0;
for channel in channels.iter() {
sum += *channel as i16;
}
p_out.push((sum / channels.len() as i16) as u8);
}, |a| a)
}
pub fn rgb_to_grayscale_f32(input: &Image<f32>) -> Image<f32> {
input.map_pixels_if_alpha(|channels, p_out| {
let mut sum = 0.0;
for channel in channels.iter() {
sum += *channel;
}
p_out.push(sum / channels.len() as f32);
}, |a| a)
}
pub fn linearize_srgb_f32(input: &Image<u8>) -> Image<f32> {
let mut lookup_table: [f32; 256] = [0.0; 256];
util::generate_lookup_table(&mut lookup_table, |i| {
let val = i as f32;
if val <= 10.0 {
val / 3294.0
} else {
((val + 14.025) / 269.025).powf(GAMMA)
}
});
input.map_channels_if_alpha(|i| lookup_table[i as usize], |a| a as f32)
}
pub fn unlinearize_srgb_f32(input: &Image<f32>) -> Image<u8> {
input.map_channels_if_alpha(|num| {
if num <= 0.0031308 {
(num * 3294.6) as u8
} else {
(269.025 * num.powf(1.0 / GAMMA) - 14.025) as u8
}
}, |a| a.round() as u8)
}
pub fn srgb_lin_to_xyz_f32(input: &Image<f32>) -> Image<f32> {
input.map_pixels_if_alpha(|channels, p_out| {
util::vector_mul_mut(&SRGB_TO_XYZ_MAT, channels, p_out).unwrap()
}, |a| a)
}
pub fn xyz_to_srgb_lin_f32(input: &Image<f32>) -> Image<f32> {
input.map_pixels_if_alpha(|channels, p_out| {
util::vector_mul_mut(&XYZ_TO_SRGB_MAT, channels, p_out).unwrap()
}, |a| a)
}
pub fn xyz_to_lab_f32(input: &Image<f32>, ref_white: &White) -> Image<f32> {
let (x_n, y_n, z_n) = util::xyz_tristimulus_vals(ref_white);
input.map_pixels_if_alpha(|channels, p_out| {
let x = util::xyz_to_lab_fn(channels[0] * 100.0 / x_n);
let y = util::xyz_to_lab_fn(channels[1] * 100.0 / y_n);
let z = util::xyz_to_lab_fn(channels[2] * 100.0 / z_n);
p_out.extend([116.0 * y - 16.0,
500.0 * (x - y),
200.0 * (y - z)].iter());
}, |a| a)
}
pub fn lab_to_xyz_f32(input: &Image<f32>, ref_white: &White) -> Image<f32> {
let (x_n, y_n, z_n) = util::xyz_tristimulus_vals(ref_white);
input.map_pixels_if_alpha(|channels, p_out| {
let n = (channels[0] + 16.0) / 116.0;
p_out.extend([x_n * util::lab_to_xyz_fn(n + channels[1] / 500.0) / 100.0,
y_n * util::lab_to_xyz_fn(n) / 100.0,
z_n * util::lab_to_xyz_fn(n - channels[2] / 200.0) / 100.0].iter());
}, |a| a)
}
pub fn rgb_to_hsv(input: &Image<u8>) -> Image<u8> {
input.map_pixels_if_alpha(|channels, p_out| {
let max = cmp::max(cmp::max(channels[0], channels[1]), channels[2]) as i32;
let min = cmp::min(cmp::min(channels[0], channels[1]), channels[2]) as i32;
let range = max - min;
let r = channels[0] as i32;
let g = channels[1] as i32;
let b = channels[2] as i32;
let mut saturation = 0;
if max != 0 {
saturation = 255 * range / max;
}
let mut hue: i32 = 0;
if range != 0 {
if max == r {
hue = 43 * (g - b) / range;
} else if max == g {
hue = 85 + 43 * (b - r) / range;
} else {
hue = 170 + 43 * (r - g) / range;
}
}
if hue < 0 {
hue += 255;
} else if hue > 255 {
hue -= 255;
}
p_out.extend([hue as u8, saturation as u8, max as u8].iter());
}, |a| a)
}
pub fn hsv_to_rgb(input: &Image<u8>) -> Image<u8> {
input.map_pixels_if_alpha(|channels, p_out| {
if channels[1] == 0 {
let val = channels[2];
p_out.extend([val, val, val].iter());
return;
}
let h = channels[0] as i32;
let s = channels[1] as i32;
let v = channels[2] as i32;
let hue = h / 43;
let f = (h - (hue * 43)) * 6;
let p = ((v * (255 - s)) / 255) as u8;
let q = ((v * (255 - (s * f) / 255)) / 255) as u8;
let t = ((v * (255 - (s * (255 - f)) / 255)) / 255) as u8;
match hue {
0 => p_out.extend([v as u8, t, p].iter()),
1 => p_out.extend([q, v as u8, p].iter()),
2 => p_out.extend([p, v as u8, t].iter()),
3 => p_out.extend([p, q, v as u8].iter()),
4 => p_out.extend([t, p, v as u8].iter()),
_ => p_out.extend([v as u8, p, q].iter()),
}
}, |a| a)
}
pub fn rgb_to_hsv_f32(input: &Image<u8>) -> Image<f32> {
input.map_pixels_if_alpha(|channels, p_out| {
let max: u8 = cmp::max(cmp::max(channels[0], channels[1]), channels[2]);
let min: u8 = cmp::min(cmp::min(channels[0], channels[1]), channels[2]);
let range = (max - min) as f32 / 255.0;
let r = channels[0] as f32 / 255.0;
let g = channels[1] as f32 / 255.0;
let b = channels[2] as f32 / 255.0;
let mut saturation: f32 = 0.0;
if max != 0 { saturation = range / (max as f32 / 255.0); }
let mut hue = 0.0;
if range != 0.0 {
if max == channels[0] {
hue = (g - b) / range
} else if max == channels[1] {
hue = (b - r) / range + 2.0
} else {
hue = (r - g) / range + 4.0
}
}
hue /= 6.0;
if hue < 0.0 {
hue += 1.0;
} else if hue > 1.0 {
hue -= 1.0;
}
p_out.extend([hue, saturation, (max as f32) / 255.0].iter());
}, |a| (a as f32) / 255.0)
}
pub fn hsv_to_rgb_f32(input: &Image<f32>) -> Image<u8> {
input.map_pixels_if_alpha(|channels, p_out| {
if channels[1] == 0.0 {
let val = (channels[2] * 255.0) as u8;
p_out.extend([val, val, val].iter());
return;
}
let hue = channels[0] * 6.0;
let f = hue - hue.floor();
let p = (channels[2] * (1.0 - channels[1]) * 255.0) as u8;
let q = (channels[2] * (1.0 - channels[1] * f) * 255.0) as u8;
let t = (channels[2] * (1.0 - channels[1] * (1.0 - f)) * 255.0) as u8;
let val = (channels[2] * 255.0) as u8;
match hue.floor() as u8 {
0 => p_out.extend([val, t, p].iter()),
1 => p_out.extend([q, val, p].iter()),
2 => p_out.extend([p, val, t].iter()),
3 => p_out.extend([p, q, val].iter()),
4 => p_out.extend([t, p, val].iter()),
_ => p_out.extend([val, p, q].iter()),
}
}, |a| (a * 255.0).round() as u8)
}
pub fn srgb_to_xyz_f32(input: &Image<u8>) -> Image<f32> {
let linearized = linearize_srgb_f32(input);
srgb_lin_to_xyz_f32(&linearized)
}
pub fn xyz_to_srgb_f32(input: &Image<f32>) -> Image<u8> {
let srgb = xyz_to_srgb_lin_f32(input);
unlinearize_srgb_f32(&srgb)
}
pub fn srgb_to_lab_f32(input: &Image<u8>, ref_white: &White) -> Image<f32> {
let xyz = srgb_to_xyz_f32(input);
xyz_to_lab_f32(&xyz, ref_white)
}
pub fn lab_to_srgb_f32(input: &Image<f32>, ref_white: &White) -> Image<u8> {
let xyz = lab_to_xyz_f32(input, ref_white);
xyz_to_srgb_f32(&xyz)
}