diff --git a/libraries/raw-rs/src/decoder/arw1.rs b/libraries/raw-rs/src/decoder/arw1.rs index 960d4037..aecda5da 100644 --- a/libraries/raw-rs/src/decoder/arw1.rs +++ b/libraries/raw-rs/src/decoder/arw1.rs @@ -27,6 +27,7 @@ pub fn decode_a100(ifd: Ifd, file: &mut TiffRead) -> 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, diff --git a/libraries/raw-rs/src/decoder/arw2.rs b/libraries/raw-rs/src/decoder/arw2.rs index 72ec60a7..32f03af1 100644 --- a/libraries/raw-rs/src/decoder/arw2.rs +++ b/libraries/raw-rs/src/decoder/arw2.rs @@ -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, } pub fn decode(ifd: Ifd, file: &mut TiffRead) -> RawImage { @@ -50,6 +51,7 @@ pub fn decode(ifd: Ifd, file: &mut TiffRead) -> 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, diff --git a/libraries/raw-rs/src/decoder/uncompressed.rs b/libraries/raw-rs/src/decoder/uncompressed.rs index 595407df..a2b882f8 100644 --- a/libraries/raw-rs/src/decoder/uncompressed.rs +++ b/libraries/raw-rs/src/decoder/uncompressed.rs @@ -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, } pub fn decode(ifd: Ifd, file: &mut TiffRead) -> RawImage { @@ -58,6 +59,7 @@ pub fn decode(ifd: Ifd, file: &mut TiffRead) -> 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, diff --git a/libraries/raw-rs/src/demosaicing/linear_demosaicing.rs b/libraries/raw-rs/src/demosaicing/linear_demosaicing.rs index fac164f9..81fdcda2 100644 --- a/libraries/raw-rs/src/demosaicing/linear_demosaicing.rs +++ b/libraries/raw-rs/src/demosaicing/linear_demosaicing.rs @@ -20,7 +20,8 @@ pub fn linear_demosaic(raw_image: RawImage) -> Image { } } -fn linear_demosaic_rggb(mut raw_image: RawImage) -> Image { +fn linear_demosaic_rggb(raw_image: RawImage) -> Image { + 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 { 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 { 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, diff --git a/libraries/raw-rs/src/lib.rs b/libraries/raw-rs/src/lib.rs index dba42e43..6ab2edc3 100644 --- a/libraries/raw-rs/src/lib.rs +++ b/libraries/raw-rs/src/lib.rs @@ -29,7 +29,8 @@ pub struct RawImage { pub maximum: u16, pub black: SubtractBlack, pub camera_model: Option, - 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 { /// See 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 { pub fn process_16bit(raw_image: RawImage) -> Image { 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); diff --git a/libraries/raw-rs/src/preprocessing/camera_data.rs b/libraries/raw-rs/src/preprocessing/camera_data.rs index 7fc0fcae..da1d0772 100644 --- a/libraries/raw-rs/src/preprocessing/camera_data.rs +++ b/libraries/raw-rs/src/preprocessing/camera_data.rs @@ -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); diff --git a/libraries/raw-rs/src/preprocessing/mod.rs b/libraries/raw-rs/src/preprocessing/mod.rs index 07858bc8..c0cfbce1 100644 --- a/libraries/raw-rs/src/preprocessing/mod.rs +++ b/libraries/raw-rs/src/preprocessing/mod.rs @@ -1,4 +1,3 @@ pub mod camera_data; -pub mod raw_to_image; pub mod scale_colors; pub mod subtract_black; diff --git a/libraries/raw-rs/src/preprocessing/raw_to_image.rs b/libraries/raw-rs/src/preprocessing/raw_to_image.rs deleted file mode 100644 index af2422df..00000000 --- a/libraries/raw-rs/src/preprocessing/raw_to_image.rs +++ /dev/null @@ -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 -} diff --git a/libraries/raw-rs/src/preprocessing/scale_colors.rs b/libraries/raw-rs/src/preprocessing/scale_colors.rs index fd73df54..c839f95b 100644 --- a/libraries/raw-rs/src/preprocessing/scale_colors.rs +++ b/libraries/raw-rs/src/preprocessing/scale_colors.rs @@ -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; } } diff --git a/libraries/raw-rs/src/tiff/mod.rs b/libraries/raw-rs/src/tiff/mod.rs index 7483ffb5..fd97f3f1 100644 --- a/libraries/raw-rs/src/tiff/mod.rs +++ b/libraries/raw-rs/src/tiff/mod.rs @@ -30,6 +30,7 @@ pub enum TagId { JpegLength = 0x202, SonyToneCurve = 0x7010, BlackLevel = 0x7310, + WhiteBalanceRggbLevels = 0x7313, CfaPatternDim = 0x828d, CfaPattern = 0x828e, ColorMatrix1 = 0xc621, diff --git a/libraries/raw-rs/src/tiff/tags.rs b/libraries/raw-rs/src/tiff/tags.rs index e5677de1..d5efcee7 100644 --- a/libraries/raw-rs/src/tiff/tags.rs +++ b/libraries/raw-rs/src/tiff/tags.rs @@ -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; + + const ID: TagId = TagId::BlackLevel; const NAME: &'static str = "Black Level"; } +impl SimpleTag for WhiteBalanceRggbLevels { + type Type = ConstArray; + + const ID: TagId = TagId::WhiteBalanceRggbLevels; + const NAME: &'static str = "White Balance Levels (RGGB)"; +} + pub trait Tag { type Output;