|
pub mod decoder; |
|
pub mod demosaicing; |
|
pub mod metadata; |
|
pub mod postprocessing; |
|
pub mod preprocessing; |
|
pub mod processing; |
|
pub mod tiff; |
|
|
|
use crate::metadata::identify::CameraModel; |
|
use processing::{Pixel, PixelTransform, RawPixel, RawPixelTransform}; |
|
use rawkit_proc_macros::Tag; |
|
use std::io::{Read, Seek}; |
|
use thiserror::Error; |
|
use tiff::file::TiffRead; |
|
use tiff::tags::{Compression, ImageLength, ImageWidth, Orientation, StripByteCounts, SubIfd, Tag}; |
|
use tiff::values::Transform; |
|
use tiff::{Ifd, TiffError}; |
|
|
|
pub(crate) const CHANNELS_IN_RGB: usize = 3; |
|
pub(crate) type Histogram = [[usize; 0x2000]; CHANNELS_IN_RGB]; |
|
|
|
|
|
pub enum SubtractBlack { |
|
|
|
None, |
|
|
|
|
|
Value(u16), |
|
|
|
|
|
CfaGrid([u16; 4]), |
|
} |
|
|
|
|
|
pub struct RawImage { |
|
|
|
pub data: Vec<u16>, |
|
|
|
|
|
pub width: usize, |
|
|
|
|
|
pub height: usize, |
|
|
|
|
|
|
|
|
|
pub cfa_pattern: [u8; 4], |
|
|
|
|
|
pub transform: Transform, |
|
|
|
|
|
pub maximum: u16, |
|
|
|
|
|
|
|
|
|
pub black: SubtractBlack, |
|
|
|
|
|
pub camera_model: Option<CameraModel>, |
|
|
|
|
|
|
|
|
|
pub camera_white_balance: Option<[f64; 4]>, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub white_balance: Option<[f64; 4]>, |
|
|
|
|
|
pub camera_to_rgb: Option<[[f64; 3]; 3]>, |
|
} |
|
|
|
|
|
pub struct Image<T> { |
|
|
|
pub data: Vec<T>, |
|
|
|
|
|
pub width: usize, |
|
|
|
|
|
pub height: usize, |
|
|
|
|
|
|
|
|
|
|
|
pub channels: u8, |
|
|
|
|
|
|
|
|
|
pub transform: Transform, |
|
} |
|
|
|
#[allow(dead_code)] |
|
#[derive(Tag)] |
|
struct ArwIfd { |
|
image_width: ImageWidth, |
|
image_height: ImageLength, |
|
compression: Compression, |
|
strip_byte_counts: StripByteCounts, |
|
} |
|
|
|
impl RawImage { |
|
|
|
|
|
|
|
pub fn decode<R: Read + Seek>(reader: &mut R) -> Result<RawImage, DecoderError> { |
|
let mut file = TiffRead::new(reader)?; |
|
let ifd = Ifd::new_first_ifd(&mut file)?; |
|
|
|
let camera_model = metadata::identify::identify_camera_model(&ifd, &mut file).unwrap(); |
|
let transform = ifd.get_value::<Orientation, _>(&mut file)?; |
|
|
|
let mut raw_image = if camera_model.model == "DSLR-A100" { |
|
decoder::arw1::decode_a100(ifd, &mut file) |
|
} else { |
|
let sub_ifd = ifd.get_value::<SubIfd, _>(&mut file)?; |
|
let arw_ifd = sub_ifd.get_value::<ArwIfd, _>(&mut file)?; |
|
|
|
if arw_ifd.compression == 1 { |
|
decoder::uncompressed::decode(sub_ifd, &mut file) |
|
} else if arw_ifd.strip_byte_counts[0] == arw_ifd.image_width * arw_ifd.image_height { |
|
decoder::arw2::decode(sub_ifd, &mut file) |
|
} else { |
|
|
|
todo!() |
|
} |
|
}; |
|
|
|
raw_image.camera_model = Some(camera_model); |
|
raw_image.transform = transform; |
|
|
|
raw_image.calculate_conversion_matrices(); |
|
|
|
Ok(raw_image) |
|
} |
|
|
|
|
|
|
|
|
|
pub fn process_8bit(self) -> Image<u8> { |
|
let image = self.process_16bit(); |
|
|
|
Image { |
|
channels: image.channels, |
|
data: image.data.iter().map(|x| (x >> 8) as u8).collect(), |
|
width: image.width, |
|
height: image.height, |
|
transform: image.transform, |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
pub fn process_16bit(self) -> Image<u16> { |
|
let subtract_black = self.subtract_black_fn(); |
|
let scale_white_balance = self.scale_white_balance_fn(); |
|
let scale_to_16bit = self.scale_to_16bit_fn(); |
|
let raw_image = self.apply((subtract_black, scale_white_balance, scale_to_16bit)); |
|
|
|
let convert_to_rgb = raw_image.convert_to_rgb_fn(); |
|
let mut record_histogram = raw_image.record_histogram_fn(); |
|
let image = raw_image.demosaic_and_apply((convert_to_rgb, &mut record_histogram)); |
|
|
|
let gamma_correction = image.gamma_correction_fn(&record_histogram.histogram); |
|
if image.transform == Transform::Horizontal { |
|
image.apply(gamma_correction) |
|
} else { |
|
image.transform_and_apply(gamma_correction) |
|
} |
|
} |
|
} |
|
|
|
impl RawImage { |
|
pub fn apply(mut self, mut transform: impl RawPixelTransform) -> RawImage { |
|
for (index, value) in self.data.iter_mut().enumerate() { |
|
let pixel = RawPixel { |
|
value: *value, |
|
row: index / self.width, |
|
column: index % self.width, |
|
}; |
|
*value = transform.apply(pixel); |
|
} |
|
|
|
self |
|
} |
|
|
|
pub fn demosaic_and_apply(self, mut transform: impl PixelTransform) -> Image<u16> { |
|
let mut image = vec![0; self.width * self.height * 3]; |
|
for Pixel { values, row, column } in self.linear_demosaic_iter().map(|mut pixel| { |
|
pixel.values = transform.apply(pixel); |
|
pixel |
|
}) { |
|
let pixel_index = row * self.width + column; |
|
image[3 * pixel_index..3 * (pixel_index + 1)].copy_from_slice(&values); |
|
} |
|
|
|
Image { |
|
channels: 3, |
|
data: image, |
|
width: self.width, |
|
height: self.height, |
|
transform: self.transform, |
|
} |
|
} |
|
} |
|
|
|
impl Image<u16> { |
|
pub fn apply(mut self, mut transform: impl PixelTransform) -> Image<u16> { |
|
for (index, values) in self.data.chunks_exact_mut(3).enumerate() { |
|
let pixel = Pixel { |
|
values: values.try_into().unwrap(), |
|
row: index / self.width, |
|
column: index % self.width, |
|
}; |
|
values.copy_from_slice(&transform.apply(pixel)); |
|
} |
|
|
|
self |
|
} |
|
|
|
pub fn transform_and_apply(self, mut transform: impl PixelTransform) -> Image<u16> { |
|
let mut image = vec![0; self.width * self.height * 3]; |
|
let (width, height, iter) = self.transform_iter(); |
|
for Pixel { values, row, column } in iter.map(|mut pixel| { |
|
pixel.values = transform.apply(pixel); |
|
pixel |
|
}) { |
|
let pixel_index = row * width + column; |
|
image[3 * pixel_index..3 * (pixel_index + 1)].copy_from_slice(&values); |
|
} |
|
|
|
Image { |
|
channels: 3, |
|
data: image, |
|
width, |
|
height, |
|
transform: Transform::Horizontal, |
|
} |
|
} |
|
} |
|
|
|
#[derive(Error, Debug)] |
|
pub enum DecoderError { |
|
#[error("An error occurred when trying to parse the TIFF format")] |
|
TiffError(#[from] TiffError), |
|
#[error("An error occurred when converting integer from one type to another")] |
|
ConversionError(#[from] std::num::TryFromIntError), |
|
#[error("An IO Error ocurred")] |
|
IoError(#[from] std::io::Error), |
|
} |
|
|