mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-21 23:28:12 +08:00
Raw-rs: add post-processing steps (#1923)
* add convert_to_rgb step * add code to generate gamma correction curve * add gamma correction step * fix clippy warnings and cargo fmt * remove unnecessary dependencies * Code review 1 * Code review 2 * fix the order of operations * Code review 3 --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
co-authored by
Keavon Chambers
parent
40fd4473a7
commit
a7840b252d
@@ -0,0 +1,40 @@
|
||||
use crate::Image;
|
||||
|
||||
const CHANNELS_IN_RGB: usize = 3;
|
||||
|
||||
pub fn convert_to_rgb(mut image: Image<u16>) -> Image<u16> {
|
||||
let Some(rgb_to_camera) = image.rgb_to_camera else { return image };
|
||||
|
||||
// Rarely this might be 4 instead of 3 if an obscure Bayer filter is used, such as RGBE or CYGM, instead of the typical RGGB.
|
||||
// See: <https://github.com/GraphiteEditor/Graphite/pull/1923#discussion_r1725070342>.
|
||||
let channels = image.channels as usize;
|
||||
let mut data = Vec::with_capacity(CHANNELS_IN_RGB * image.width * image.height);
|
||||
let mut histogram = [[0; 0x2000]; CHANNELS_IN_RGB];
|
||||
|
||||
for i in 0..(image.height * image.width) {
|
||||
let start = i * channels;
|
||||
let end = start + channels;
|
||||
let input_pixel = &mut image.data[start..end];
|
||||
|
||||
let mut output_pixel = [0.; CHANNELS_IN_RGB];
|
||||
for (channel, &value) in input_pixel.iter().enumerate() {
|
||||
output_pixel[0] += rgb_to_camera[0][channel] * value as f64;
|
||||
output_pixel[1] += rgb_to_camera[1][channel] * value as f64;
|
||||
output_pixel[2] += rgb_to_camera[2][channel] * value as f64;
|
||||
}
|
||||
|
||||
for (output_pixel_channel, histogram_channel) in output_pixel.iter().zip(histogram.iter_mut()) {
|
||||
let final_sum = (*output_pixel_channel as u16).clamp(0, u16::MAX);
|
||||
|
||||
histogram_channel[final_sum as usize >> CHANNELS_IN_RGB] += 1;
|
||||
|
||||
data.push(final_sum);
|
||||
}
|
||||
}
|
||||
|
||||
image.data = data;
|
||||
image.histogram = Some(histogram);
|
||||
image.channels = CHANNELS_IN_RGB as u8;
|
||||
|
||||
image
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
use crate::Image;
|
||||
use std::f64::consts::E;
|
||||
|
||||
pub fn gamma_correction(mut image: Image<u16>) -> Image<u16> {
|
||||
let Some(histogram) = image.histogram else { return image };
|
||||
|
||||
let percentage = image.width * image.height;
|
||||
|
||||
let mut white = 0;
|
||||
for channel_histogram in histogram {
|
||||
let mut total = 0;
|
||||
for i in (0x20..0x2000).rev() {
|
||||
total += channel_histogram[i] as u64;
|
||||
|
||||
if total * 100 > percentage as u64 {
|
||||
white = white.max(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let curve = generate_gamma_curve(0.45, 4.5, (white << 3) as f64);
|
||||
|
||||
for value in image.data.iter_mut() {
|
||||
*value = curve[*value as usize];
|
||||
}
|
||||
image.histogram = None;
|
||||
|
||||
image
|
||||
}
|
||||
|
||||
/// `max_intensity` must be non-zero.
|
||||
fn generate_gamma_curve(power: f64, threshold: f64, max_intensity: f64) -> Vec<u16> {
|
||||
debug_assert!(max_intensity != 0.);
|
||||
|
||||
let (mut bound_start, mut bound_end) = if threshold >= 1. { (0., 1.) } else { (1., 0.) };
|
||||
|
||||
let mut transition_point = 0.;
|
||||
let mut transition_ratio = 0.;
|
||||
let mut curve_adjustment = 0.;
|
||||
|
||||
if threshold != 0. && (threshold - 1.) * (power - 1.) <= 0. {
|
||||
for _ in 0..48 {
|
||||
transition_point = (bound_start + bound_end) / 2.;
|
||||
|
||||
if power != 0. {
|
||||
let temp_transition_ratio = transition_point / threshold;
|
||||
let exponential_power = temp_transition_ratio.powf(-power);
|
||||
let normalized_exponential_power = (exponential_power - 1.) / power;
|
||||
let comparison_result = normalized_exponential_power - (1. / transition_point);
|
||||
|
||||
let bound_to_update = if comparison_result > -1. { &mut bound_end } else { &mut bound_start };
|
||||
*bound_to_update = transition_point;
|
||||
} else {
|
||||
let adjusted_transition_point = E.powf(1. - 1. / transition_point);
|
||||
let transition_point_ratio = transition_point / adjusted_transition_point;
|
||||
|
||||
let bound_to_update = if transition_point_ratio < threshold { &mut bound_end } else { &mut bound_start };
|
||||
*bound_to_update = transition_point;
|
||||
}
|
||||
}
|
||||
|
||||
transition_ratio = transition_point / threshold;
|
||||
|
||||
if power != 0. {
|
||||
curve_adjustment = transition_point * ((1. / power) - 1.);
|
||||
}
|
||||
}
|
||||
|
||||
let mut curve = vec![0xffff; 0x1_0000];
|
||||
let length = curve.len() as f64;
|
||||
|
||||
for (i, entry) in curve.iter_mut().enumerate() {
|
||||
let ratio = (i as f64) / max_intensity;
|
||||
if ratio < 1. {
|
||||
let altered_ratio = if ratio < transition_ratio {
|
||||
ratio * threshold
|
||||
} else if power != 0. {
|
||||
ratio.powf(power) * (1. + curve_adjustment) - curve_adjustment
|
||||
} else {
|
||||
ratio.ln() * transition_point + 1.
|
||||
};
|
||||
|
||||
*entry = (length * altered_ratio) as u16;
|
||||
}
|
||||
}
|
||||
|
||||
curve
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod convert_to_rgb;
|
||||
pub mod gamma_correction;
|
||||
Reference in New Issue
Block a user