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:
Elbert Ronnie 2024-09-02 18:39:01 +05:30 committed by GitHub
parent 444929adbf
commit 82ef5c83ae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 51 additions and 52 deletions

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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);

View file

@ -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);

View file

@ -1,4 +1,3 @@
pub mod camera_data;
pub mod raw_to_image;
pub mod scale_colors;
pub mod subtract_black;

View file

@ -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
}

View file

@ -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;
}
}

View file

@ -30,6 +30,7 @@ pub enum TagId {
JpegLength = 0x202,
SonyToneCurve = 0x7010,
BlackLevel = 0x7310,
WhiteBalanceRggbLevels = 0x7313,
CfaPatternDim = 0x828d,
CfaPattern = 0x828e,
ColorMatrix1 = 0xc621,

View file

@ -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;