mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-07-07 15:55:00 +00:00
Raw-rs: use camera white balance when available (#1941)
* add new White Balance Tag * merge raw_to_image step with demosiacing * Use white balance from camera in scale colors step * Nit --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
444929adbf
commit
82ef5c83ae
11 changed files with 51 additions and 52 deletions
|
@ -27,6 +27,7 @@ pub fn decode_a100<R: Read + Seek>(ifd: Ifd, file: &mut TiffRead<R>) -> RawImage
|
|||
maximum: (1 << 12) - 1,
|
||||
black: SubtractBlack::None,
|
||||
camera_model: None,
|
||||
camera_white_balance_multiplier: None,
|
||||
white_balance_multiplier: None,
|
||||
camera_to_rgb: None,
|
||||
rgb_to_camera: None,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::tiff::file::{Endian, TiffRead};
|
||||
use crate::tiff::tags::{BitsPerSample, CfaPattern, CfaPatternDim, Compression, ImageLength, ImageWidth, SonyToneCurve, StripByteCounts, StripOffsets, Tag};
|
||||
use crate::tiff::tags::{BitsPerSample, CfaPattern, CfaPatternDim, Compression, ImageLength, ImageWidth, SonyToneCurve, StripByteCounts, StripOffsets, Tag, WhiteBalanceRggbLevels};
|
||||
use crate::tiff::values::CurveLookupTable;
|
||||
use crate::tiff::{Ifd, TiffError};
|
||||
use crate::{RawImage, SubtractBlack};
|
||||
|
@ -19,6 +19,7 @@ struct Arw2Ifd {
|
|||
strip_offsets: StripOffsets,
|
||||
strip_byte_counts: StripByteCounts,
|
||||
sony_tone_curve: SonyToneCurve,
|
||||
white_balance_levels: Option<WhiteBalanceRggbLevels>,
|
||||
}
|
||||
|
||||
pub fn decode<R: Read + Seek>(ifd: Ifd, file: &mut TiffRead<R>) -> RawImage {
|
||||
|
@ -50,6 +51,7 @@ pub fn decode<R: Read + Seek>(ifd: Ifd, file: &mut TiffRead<R>) -> RawImage {
|
|||
maximum: (1 << 14) - 1,
|
||||
black: SubtractBlack::CfaGrid([512, 512, 512, 512]), // TODO: Find the correct way to do this
|
||||
camera_model: None,
|
||||
camera_white_balance_multiplier: ifd.white_balance_levels.map(|arr| arr.map(|x| x as f64)),
|
||||
white_balance_multiplier: None,
|
||||
camera_to_rgb: None,
|
||||
rgb_to_camera: None,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::tiff::file::TiffRead;
|
||||
use crate::tiff::tags::{BitsPerSample, BlackLevel, CfaPattern, CfaPatternDim, Compression, ImageLength, ImageWidth, RowsPerStrip, StripByteCounts, StripOffsets, Tag};
|
||||
use crate::tiff::tags::{BitsPerSample, BlackLevel, CfaPattern, CfaPatternDim, Compression, ImageLength, ImageWidth, RowsPerStrip, StripByteCounts, StripOffsets, Tag, WhiteBalanceRggbLevels};
|
||||
use crate::tiff::{Ifd, TiffError};
|
||||
use crate::{RawImage, SubtractBlack};
|
||||
|
||||
|
@ -19,6 +19,7 @@ struct ArwUncompressedIfd {
|
|||
cfa_pattern_dim: CfaPatternDim,
|
||||
strip_offsets: StripOffsets,
|
||||
strip_byte_counts: StripByteCounts,
|
||||
white_balance_levels: Option<WhiteBalanceRggbLevels>,
|
||||
}
|
||||
|
||||
pub fn decode<R: Read + Seek>(ifd: Ifd, file: &mut TiffRead<R>) -> RawImage {
|
||||
|
@ -58,6 +59,7 @@ pub fn decode<R: Read + Seek>(ifd: Ifd, file: &mut TiffRead<R>) -> RawImage {
|
|||
maximum: if bits_per_sample == 16 { u16::MAX } else { (1 << bits_per_sample) - 1 },
|
||||
black: SubtractBlack::CfaGrid(ifd.black_level),
|
||||
camera_model: None,
|
||||
camera_white_balance_multiplier: ifd.white_balance_levels.map(|arr| arr.map(|x| x as f64)),
|
||||
white_balance_multiplier: None,
|
||||
camera_to_rgb: None,
|
||||
rgb_to_camera: None,
|
||||
|
|
|
@ -20,7 +20,8 @@ pub fn linear_demosaic(raw_image: RawImage) -> Image<u16> {
|
|||
}
|
||||
}
|
||||
|
||||
fn linear_demosaic_rggb(mut raw_image: RawImage) -> Image<u16> {
|
||||
fn linear_demosaic_rggb(raw_image: RawImage) -> Image<u16> {
|
||||
let mut image = vec![0; raw_image.width * raw_image.height * 3];
|
||||
let width = raw_image.width as i64;
|
||||
let height = raw_image.height as i64;
|
||||
|
||||
|
@ -35,34 +36,27 @@ fn linear_demosaic_rggb(mut raw_image: RawImage) -> Image<u16> {
|
|||
let cross_indexes = [pixel_index + width, pixel_index - width, pixel_index + 1, pixel_index - 1];
|
||||
let diagonal_indexes = [pixel_index + width + 1, pixel_index - width + 1, pixel_index + width - 1, pixel_index - width - 1];
|
||||
|
||||
let pixel_index = pixel_index as usize;
|
||||
match (row % 2 == 0, col % 2 == 0) {
|
||||
(true, true) => {
|
||||
let indexes = cross_indexes.iter().map(|x| 3 * x + 1);
|
||||
raw_image.data[3 * (pixel_index as usize) + 1] = average(&raw_image.data, indexes);
|
||||
|
||||
let indexes = diagonal_indexes.iter().map(|x| 3 * x + 2);
|
||||
raw_image.data[3 * (pixel_index as usize) + 2] = average(&raw_image.data, indexes);
|
||||
image[3 * pixel_index] = raw_image.data[pixel_index];
|
||||
image[3 * pixel_index + 1] = average(&raw_image.data, cross_indexes.into_iter());
|
||||
image[3 * pixel_index + 2] = average(&raw_image.data, diagonal_indexes.into_iter());
|
||||
}
|
||||
(true, false) => {
|
||||
let indexes = horizontal_indexes.iter().map(|x| 3 * x);
|
||||
raw_image.data[3 * (pixel_index as usize)] = average(&raw_image.data, indexes);
|
||||
|
||||
let indexes = vertical_indexes.iter().map(|x| 3 * x + 2);
|
||||
raw_image.data[3 * (pixel_index as usize) + 2] = average(&raw_image.data, indexes);
|
||||
image[3 * pixel_index] = average(&raw_image.data, horizontal_indexes.into_iter());
|
||||
image[3 * pixel_index + 1] = raw_image.data[pixel_index];
|
||||
image[3 * pixel_index + 2] = average(&raw_image.data, vertical_indexes.into_iter());
|
||||
}
|
||||
(false, true) => {
|
||||
let indexes = vertical_indexes.iter().map(|x| 3 * x);
|
||||
raw_image.data[3 * (pixel_index as usize)] = average(&raw_image.data, indexes);
|
||||
|
||||
let indexes = horizontal_indexes.iter().map(|x| 3 * x + 2);
|
||||
raw_image.data[3 * (pixel_index as usize) + 2] = average(&raw_image.data, indexes);
|
||||
image[3 * pixel_index] = average(&raw_image.data, vertical_indexes.into_iter());
|
||||
image[3 * pixel_index + 1] = raw_image.data[pixel_index];
|
||||
image[3 * pixel_index + 2] = average(&raw_image.data, horizontal_indexes.into_iter());
|
||||
}
|
||||
(false, false) => {
|
||||
let indexes = cross_indexes.iter().map(|x| 3 * x + 1);
|
||||
raw_image.data[3 * (pixel_index as usize) + 1] = average(&raw_image.data, indexes);
|
||||
|
||||
let indexes = diagonal_indexes.iter().map(|x| 3 * x);
|
||||
raw_image.data[3 * (pixel_index as usize)] = average(&raw_image.data, indexes);
|
||||
image[3 * pixel_index] = average(&raw_image.data, diagonal_indexes.into_iter());
|
||||
image[3 * pixel_index + 1] = average(&raw_image.data, cross_indexes.into_iter());
|
||||
image[3 * pixel_index + 2] = raw_image.data[pixel_index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +64,7 @@ fn linear_demosaic_rggb(mut raw_image: RawImage) -> Image<u16> {
|
|||
|
||||
Image {
|
||||
channels: 3,
|
||||
data: raw_image.data,
|
||||
data: image,
|
||||
width: raw_image.width,
|
||||
height: raw_image.height,
|
||||
rgb_to_camera: raw_image.rgb_to_camera,
|
||||
|
|
|
@ -29,7 +29,8 @@ pub struct RawImage {
|
|||
pub maximum: u16,
|
||||
pub black: SubtractBlack,
|
||||
pub camera_model: Option<CameraModel>,
|
||||
pub white_balance_multiplier: Option<[f64; 3]>,
|
||||
pub camera_white_balance_multiplier: Option<[f64; 4]>,
|
||||
pub white_balance_multiplier: Option<[f64; 4]>,
|
||||
pub camera_to_rgb: Option<[[f64; 3]; 3]>,
|
||||
pub rgb_to_camera: Option<[[f64; 3]; 3]>,
|
||||
}
|
||||
|
@ -42,7 +43,7 @@ pub struct Image<T> {
|
|||
/// See <https://github.com/GraphiteEditor/Graphite/pull/1923#discussion_r1725070342> for more information.
|
||||
pub channels: u8,
|
||||
pub rgb_to_camera: Option<[[f64; 3]; 3]>,
|
||||
pub histogram: Option<[[usize; 0x2000]; 3]>,
|
||||
pub(crate) histogram: Option<[[usize; 0x2000]; 3]>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
@ -97,7 +98,6 @@ pub fn process_8bit(raw_image: RawImage) -> Image<u8> {
|
|||
pub fn process_16bit(raw_image: RawImage) -> Image<u16> {
|
||||
let raw_image = crate::preprocessing::camera_data::calculate_conversion_matrices(raw_image);
|
||||
let raw_image = crate::preprocessing::subtract_black::subtract_black(raw_image);
|
||||
let raw_image = crate::preprocessing::raw_to_image::raw_to_image(raw_image);
|
||||
let raw_image = crate::preprocessing::scale_colors::scale_colors(raw_image);
|
||||
let image = crate::demosaicing::linear_demosaicing::linear_demosaic(raw_image);
|
||||
let image = crate::postprocessing::convert_to_rgb::convert_to_rgb(image);
|
||||
|
|
|
@ -49,7 +49,13 @@ pub fn calculate_conversion_matrices(mut raw_image: RawImage) -> RawImage {
|
|||
}
|
||||
let rgb_to_camera = transpose(pseudoinverse(camera_to_rgb));
|
||||
|
||||
raw_image.white_balance_multiplier = Some(white_balance_multiplier);
|
||||
let cfa_white_balance_multiplier = if let Some(white_balance) = raw_image.camera_white_balance_multiplier {
|
||||
white_balance
|
||||
} else {
|
||||
raw_image.cfa_pattern.map(|index| white_balance_multiplier[index as usize])
|
||||
};
|
||||
|
||||
raw_image.white_balance_multiplier = Some(cfa_white_balance_multiplier);
|
||||
raw_image.camera_to_rgb = Some(camera_to_rgb);
|
||||
raw_image.rgb_to_camera = Some(rgb_to_camera);
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
pub mod camera_data;
|
||||
pub mod raw_to_image;
|
||||
pub mod scale_colors;
|
||||
pub mod subtract_black;
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
use crate::RawImage;
|
||||
|
||||
pub fn raw_to_image(mut raw_image: RawImage) -> RawImage {
|
||||
let mut image = Vec::with_capacity(raw_image.width * raw_image.height * 3);
|
||||
|
||||
for row in 0..raw_image.height {
|
||||
for col in 0..raw_image.width {
|
||||
let mut pixel = [0_u16; 3];
|
||||
let color_index = raw_image.cfa_pattern[2 * (row % 2) + (col % 2)];
|
||||
pixel[color_index as usize] = raw_image.data[row * raw_image.width + col];
|
||||
image.extend_from_slice(&pixel);
|
||||
}
|
||||
}
|
||||
|
||||
raw_image.data = image;
|
||||
raw_image
|
||||
}
|
|
@ -22,12 +22,14 @@ pub fn scale_colors(mut raw_image: RawImage) -> RawImage {
|
|||
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.]
|
||||
[1., 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 row in 0..raw_image.height {
|
||||
for column in 0..raw_image.width {
|
||||
let index = row * raw_image.width + column;
|
||||
let cfa_index = 2 * (row % 2) + (column % 2);
|
||||
raw_image.data[index] = ((raw_image.data[index] as f64) * final_multiplier[cfa_index]).min(u16::MAX as f64).max(0.) as u16;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ pub enum TagId {
|
|||
JpegLength = 0x202,
|
||||
SonyToneCurve = 0x7010,
|
||||
BlackLevel = 0x7310,
|
||||
WhiteBalanceRggbLevels = 0x7313,
|
||||
CfaPatternDim = 0x828d,
|
||||
CfaPattern = 0x828e,
|
||||
ColorMatrix1 = 0xc621,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use super::types::{Array, ConstArray, TagType, TypeByte, TypeIfd, TypeLong, TypeNumber, TypeSRational, TypeShort, TypeSonyToneCurve, TypeString};
|
||||
use super::types::{Array, ConstArray, TagType, TypeByte, TypeIfd, TypeLong, TypeNumber, TypeSRational, TypeSShort, TypeShort, TypeSonyToneCurve, TypeString};
|
||||
use super::{Ifd, TagId, TiffError, TiffRead};
|
||||
|
||||
use std::io::{Read, Seek};
|
||||
|
@ -27,6 +27,7 @@ pub struct JpegLength;
|
|||
pub struct SonyDataOffset;
|
||||
pub struct SonyToneCurve;
|
||||
pub struct BlackLevel;
|
||||
pub struct WhiteBalanceRggbLevels;
|
||||
pub struct CfaPatternDim;
|
||||
pub struct CfaPattern;
|
||||
pub struct ColorMatrix1;
|
||||
|
@ -173,11 +174,19 @@ impl SimpleTag for SonyToneCurve {
|
|||
}
|
||||
|
||||
impl SimpleTag for BlackLevel {
|
||||
const ID: TagId = TagId::BlackLevel;
|
||||
type Type = ConstArray<TypeShort, 4>;
|
||||
|
||||
const ID: TagId = TagId::BlackLevel;
|
||||
const NAME: &'static str = "Black Level";
|
||||
}
|
||||
|
||||
impl SimpleTag for WhiteBalanceRggbLevels {
|
||||
type Type = ConstArray<TypeSShort, 4>;
|
||||
|
||||
const ID: TagId = TagId::WhiteBalanceRggbLevels;
|
||||
const NAME: &'static str = "White Balance Levels (RGGB)";
|
||||
}
|
||||
|
||||
pub trait Tag {
|
||||
type Output;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue