pub fn create_synthetic_rgb(band1_data: &[u8], band2_data: &[u8]) -> Vec<u8> {
debug_assert_eq!(band1_data.len(), band2_data.len());
const GAMMA_R: f32 = 0.7;
const GAMMA_G: f32 = 0.9;
const GAMMA_B: f32 = 0.1;
const SCALE_255: f32 = 255.0;
const BLUE_SCALE: f32 = 0.24;
let mut lut_r = [0u8; 256];
let mut lut_g = [0u8; 256];
for v in 0u16..=255 {
let vf = (v as f32) / SCALE_255;
let r = (vf.powf(GAMMA_R) * SCALE_255).round().clamp(0.0, 255.0) as u8;
let g = (vf.powf(GAMMA_G) * SCALE_255).round().clamp(0.0, 255.0) as u8;
lut_r[v as usize] = r;
lut_g[v as usize] = g;
}
let mut lut_b = [0u8; 256 * 256];
for b1 in 0u16..=255 {
for b2 in 0u16..=255 {
let idx = (b1 as usize) << 8 | (b2 as usize);
let blue = if b2 == 0 {
0u8
} else {
let r = lut_r[b1 as usize] as f32;
let g = lut_g[b2 as usize] as f32;
let ratio = r / g; (ratio.powf(GAMMA_B) * SCALE_255 * BLUE_SCALE)
.clamp(0.0, 255.0)
.round() as u8
};
lut_b[idx] = blue;
}
}
let mut rgb_data = Vec::with_capacity(band1_data.len() * 3);
for i in 0..band1_data.len() {
let v1 = band1_data[i] as usize;
let v2 = band2_data[i] as usize;
let r = lut_r[v1];
let g = lut_g[v2];
let b = lut_b[(v1 << 8) | v2];
rgb_data.push(r);
rgb_data.push(g);
rgb_data.push(b);
}
rgb_data
}
use crate::types::{AutoscaleStrategy, SyntheticRgbMode};
pub fn create_synthetic_rgb_by_mode(mode: SyntheticRgbMode, band1_data: &[u8], band2_data: &[u8]) -> Vec<u8> {
match mode {
SyntheticRgbMode::Default => create_synthetic_rgb(band1_data, band2_data),
SyntheticRgbMode::RgbRatio => create_synthetic_rgb(band1_data, band2_data),
SyntheticRgbMode::SarUrban => create_synthetic_rgb(band1_data, band2_data),
SyntheticRgbMode::Enhanced => create_synthetic_rgb(band1_data, band2_data),
}
}
pub fn create_synthetic_rgb_suppressed(band1_data: &[u8], band2_data: &[u8]) -> Vec<u8> {
debug_assert_eq!(band1_data.len(), band2_data.len());
let mut histogram = [0u32; 256];
for &v in band1_data.iter() {
histogram[v as usize] = histogram[v as usize].saturating_add(1);
}
for &v in band2_data.iter() {
histogram[v as usize] = histogram[v as usize].saturating_add(1);
}
let total_count: u32 = (band1_data.len() + band2_data.len()) as u32;
let target_count: u32 = ((total_count as f64) * 0.05).round() as u32; let mut cumulative: u32 = 0;
let mut floor_value: usize = 0;
for i in 0..=255 {
cumulative = cumulative.saturating_add(histogram[i]);
if cumulative >= target_count {
floor_value = i;
break;
}
}
let floor_with_cushion: u8 = floor_value
.saturating_add(3) .min(40) as u8;
const SCALE_255: f32 = 255.0;
const GAMMA_R_SUPP: f32 = 1.15; const GAMMA_G_SUPP: f32 = 1.10;
let floor = floor_with_cushion as f32;
let denom = (255.0 - floor).max(1.0);
let mut lut_r = [0u8; 256];
let mut lut_g = [0u8; 256];
for v in 0u16..=255 {
if (v as u8) <= floor_with_cushion {
lut_r[v as usize] = 0;
lut_g[v as usize] = 0;
} else {
let shifted = (v as f32 - floor) / denom; let r = (shifted.powf(GAMMA_R_SUPP) * SCALE_255).round().clamp(0.0, 255.0) as u8;
let g = (shifted.powf(GAMMA_G_SUPP) * SCALE_255).round().clamp(0.0, 255.0) as u8;
lut_r[v as usize] = r;
lut_g[v as usize] = g;
}
}
const GAMMA_B: f32 = 0.1;
const BLUE_SCALE_SUPP: f32 = 0.18; const EPS: f32 = 8.0;
let mut lut_b = [0u8; 256 * 256];
for b1 in 0u16..=255 {
for b2 in 0u16..=255 {
let idx = (b1 as usize) << 8 | (b2 as usize);
let r = lut_r[b1 as usize] as f32;
let g = lut_g[b2 as usize] as f32;
let ratio = (r + EPS) / (g + EPS);
let blue = (ratio.powf(GAMMA_B) * SCALE_255 * BLUE_SCALE_SUPP)
.clamp(0.0, 255.0)
.round() as u8;
lut_b[idx] = blue;
}
}
let mut rgb_data = Vec::with_capacity(band1_data.len() * 3);
for i in 0..band1_data.len() {
let v1_raw = band1_data[i];
let v2_raw = band2_data[i];
if v1_raw <= floor_with_cushion && v2_raw <= floor_with_cushion {
rgb_data.push(0);
rgb_data.push(0);
rgb_data.push(0);
continue;
}
let v1 = v1_raw as usize;
let v2 = v2_raw as usize;
let r = lut_r[v1];
let g = lut_g[v2];
let b = lut_b[(v1 << 8) | v2];
rgb_data.push(r);
rgb_data.push(g);
rgb_data.push(b);
}
rgb_data
}
pub fn create_synthetic_rgb_by_mode_and_strategy(
mode: SyntheticRgbMode,
strategy: AutoscaleStrategy,
band1_data: &[u8],
band2_data: &[u8],
) -> Vec<u8> {
match strategy {
AutoscaleStrategy::Tamed | AutoscaleStrategy::Clahe => match mode {
SyntheticRgbMode::Default
| SyntheticRgbMode::RgbRatio
| SyntheticRgbMode::SarUrban
| SyntheticRgbMode::Enhanced => create_synthetic_rgb_suppressed(band1_data, band2_data),
},
_ => create_synthetic_rgb_by_mode(mode, band1_data, band2_data),
}
}
pub fn create_synthetic_rgb_rgb_ratio(_band1_data: &[u8], _band2_data: &[u8]) -> Vec<u8> {
todo!("create_synthetic_rgb_rgb_ratio: to be implemented")
}
pub fn create_synthetic_rgb_urban(_band1_data: &[u8], _band2_data: &[u8]) -> Vec<u8> {
todo!("create_synthetic_rgb_urban: to be implemented")
}
pub fn create_synthetic_rgb_enhanced(_band1_data: &[u8], _band2_data: &[u8]) -> Vec<u8> {
todo!("create_synthetic_rgb_enhanced: to be implemented")
}