mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-22 00:48:13 +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
@@ -1,4 +1,4 @@
|
||||
use crate::metadata::identify::CameraModel;
|
||||
use crate::RawImage;
|
||||
use build_camera_data::build_camera_data;
|
||||
|
||||
pub struct CameraData {
|
||||
@@ -17,10 +17,97 @@ impl CameraData {
|
||||
|
||||
const CAMERA_DATA: [(&str, CameraData); 40] = build_camera_data!();
|
||||
|
||||
pub fn camera_to_xyz(camera_model: &CameraModel) -> Option<[f64; 9]> {
|
||||
const XYZ_TO_RGB: [[f64; 3]; 3] = [
|
||||
// Matrix:
|
||||
[0.412453, 0.357580, 0.180423],
|
||||
[0.212671, 0.715160, 0.072169],
|
||||
[0.019334, 0.119193, 0.950227],
|
||||
];
|
||||
|
||||
pub fn calculate_conversion_matrices(mut raw_image: RawImage) -> RawImage {
|
||||
let Some(ref camera_model) = raw_image.camera_model else { return raw_image };
|
||||
let camera_name_needle = camera_model.make.to_owned() + " " + &camera_model.model;
|
||||
CAMERA_DATA
|
||||
|
||||
let camera_to_xyz = CAMERA_DATA
|
||||
.iter()
|
||||
.find(|(camera_name_haystack, _)| camera_name_needle == *camera_name_haystack)
|
||||
.map(|(_, data)| data.camera_to_xyz.map(|x| (x as f64) / 10_000.))
|
||||
.map(|(_, data)| data.camera_to_xyz.map(|x| (x as f64) / 10_000.));
|
||||
let Some(camera_to_xyz) = camera_to_xyz else { return raw_image };
|
||||
|
||||
let mut camera_to_rgb = [[0.; 3]; 3];
|
||||
for i in 0..3 {
|
||||
for j in 0..3 {
|
||||
for k in 0..3 {
|
||||
camera_to_rgb[i][j] += camera_to_xyz[i * 3 + k] * XYZ_TO_RGB[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let white_balance_multiplier = camera_to_rgb.map(|x| 1. / x.iter().sum::<f64>());
|
||||
for (index, row) in camera_to_rgb.iter_mut().enumerate() {
|
||||
*row = row.map(|x| x * white_balance_multiplier[index]);
|
||||
}
|
||||
let rgb_to_camera = transpose(pseudoinverse(camera_to_rgb));
|
||||
|
||||
raw_image.white_balance_multiplier = Some(white_balance_multiplier);
|
||||
raw_image.camera_to_rgb = Some(camera_to_rgb);
|
||||
raw_image.rgb_to_camera = Some(rgb_to_camera);
|
||||
|
||||
raw_image
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
fn pseudoinverse<const N: usize>(matrix: [[f64; 3]; N]) -> [[f64; 3]; N] {
|
||||
let mut output_matrix = [[0.; 3]; N];
|
||||
let mut work = [[0.; 6]; 3];
|
||||
|
||||
for i in 0..3 {
|
||||
for j in 0..6 {
|
||||
work[i][j] = if j == i + 3 { 1. } else { 0. };
|
||||
}
|
||||
for j in 0..3 {
|
||||
for k in 0..N {
|
||||
work[i][j] += matrix[k][i] * matrix[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..3 {
|
||||
let num = work[i][i];
|
||||
for j in 0..6 {
|
||||
work[i][j] /= num;
|
||||
}
|
||||
for k in 0..3 {
|
||||
if k == i {
|
||||
continue;
|
||||
}
|
||||
let num = work[k][i];
|
||||
for j in 0..6 {
|
||||
work[k][j] -= work[i][j] * num;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..N {
|
||||
for j in 0..3 {
|
||||
output_matrix[i][j] = 0.;
|
||||
for k in 0..3 {
|
||||
output_matrix[i][j] += work[j][k + 3] * matrix[i][k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output_matrix
|
||||
}
|
||||
|
||||
fn transpose<const N: usize>(matrix: [[f64; 3]; N]) -> [[f64; N]; 3] {
|
||||
let mut output_matrix = [[0.; N]; 3];
|
||||
|
||||
for (i, row) in matrix.iter().enumerate() {
|
||||
for (j, &value) in row.iter().enumerate() {
|
||||
output_matrix[j][i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
output_matrix
|
||||
}
|
||||
|
||||
@@ -1,44 +1,33 @@
|
||||
use crate::RawImage;
|
||||
|
||||
const XYZ_TO_RGB: [[f64; 3]; 3] = [[0.412453, 0.357580, 0.180423], [0.212671, 0.715160, 0.072169], [0.019334, 0.119193, 0.950227]];
|
||||
|
||||
pub fn scale_colors(mut raw_image: RawImage) -> RawImage {
|
||||
if let Some(camera_to_xyz) = raw_image.camera_to_xyz {
|
||||
let mut camera_to_rgb = [[0.; 3]; 3];
|
||||
for i in 0..3 {
|
||||
for j in 0..3 {
|
||||
for k in 0..3 {
|
||||
camera_to_rgb[i][j] += camera_to_xyz[i * 3 + k] * XYZ_TO_RGB[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
let Some(mut white_balance_multiplier) = raw_image.white_balance_multiplier else {
|
||||
return raw_image;
|
||||
};
|
||||
|
||||
let mut white_balance_multiplier = camera_to_rgb.map(|x| 1. / x.iter().sum::<f64>());
|
||||
if white_balance_multiplier[1] == 0. {
|
||||
white_balance_multiplier[1] = 1.;
|
||||
}
|
||||
|
||||
if white_balance_multiplier[1] == 0. {
|
||||
white_balance_multiplier[1] = 1.;
|
||||
}
|
||||
// TODO: Move this at its correct location when highlights are implemented correctly.
|
||||
let highlight = 0;
|
||||
|
||||
// TODO: Move this at its correct location when highlights are implemented correctly.
|
||||
let highlight = 0;
|
||||
let normalize_white_balance = if highlight == 0 {
|
||||
white_balance_multiplier.iter().copied().fold(f64::INFINITY, f64::min)
|
||||
} else {
|
||||
white_balance_multiplier.iter().copied().fold(f64::NEG_INFINITY, f64::max)
|
||||
};
|
||||
|
||||
let normalize_white_balance = if highlight == 0 {
|
||||
white_balance_multiplier.iter().fold(f64::INFINITY, |a, &b| a.min(b))
|
||||
} else {
|
||||
white_balance_multiplier.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b))
|
||||
};
|
||||
let final_multiplier = if normalize_white_balance > 0.00001 && raw_image.maximum > 0 {
|
||||
let scale_to_16bit_multiplier = u16::MAX as f64 / raw_image.maximum as f64;
|
||||
white_balance_multiplier.map(|x| x / normalize_white_balance * scale_to_16bit_multiplier)
|
||||
} else {
|
||||
[1., 1., 1.]
|
||||
};
|
||||
|
||||
let final_multiplier = if normalize_white_balance > 0.00001 && raw_image.maximum > 0 {
|
||||
let scale_to_16bit_multiplier = u16::MAX as f64 / raw_image.maximum as f64;
|
||||
white_balance_multiplier.map(|x| x / normalize_white_balance * scale_to_16bit_multiplier)
|
||||
} else {
|
||||
[1., 1., 1.]
|
||||
};
|
||||
|
||||
for i in 0..(raw_image.height * raw_image.width) {
|
||||
for (c, multiplier) in final_multiplier.iter().enumerate() {
|
||||
raw_image.data[3 * i + c] = ((raw_image.data[3 * i + c] as f64) * multiplier).min(u16::MAX as f64).max(0.) as u16;
|
||||
}
|
||||
for i in 0..(raw_image.height * raw_image.width) {
|
||||
for (c, multiplier) in final_multiplier.iter().enumerate() {
|
||||
raw_image.data[3 * i + c] = ((raw_image.data[3 * i + c] as f64) * multiplier).min(u16::MAX as f64).max(0.) as u16;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user