Files
DarkRoom/third_party/rawler-0.7.2/src/decoders/camera.rs
T
dtourolle 77a1925bac Vendor rawler 0.7.2 unmodified
The crate as crates.io publishes it, minus .cargo-ok, its Cargo.lock and
data/testdata (13 MB of sample files only its own tests read). Not yet
routed through [patch.crates-io]; the next commit is the patch.
2026-09-27 17:33:19 -04:00

277 lines
9.6 KiB
Rust

use toml::Value;
use crate::CFA;
use crate::cfa::PlaneColor;
use crate::imgop::xyz::FlatColorMatrix;
use crate::imgop::xyz::Illuminant;
use std::collections::HashMap;
use super::BlackLevel;
use super::WhiteLevel;
/// Contains sanitized information about the raw image's properties
#[derive(Debug, Clone, Default)]
pub struct Camera {
pub make: String,
pub model: String,
pub mode: String,
pub clean_make: String,
pub clean_model: String,
pub remark: Option<String>,
pub filesize: usize,
pub raw_width: usize,
pub raw_height: usize,
//pub orientation: Orientation,
pub whitelevel: Option<Vec<u32>>,
pub blacklevel: Option<Vec<u32>>,
pub blackareah: Option<(usize, usize)>,
pub blackareav: Option<(usize, usize)>,
pub xyz_to_cam: [[f32; 3]; 4],
pub color_matrix: HashMap<Illuminant, FlatColorMatrix>,
pub cfa: CFA,
pub plane_color: PlaneColor,
// Active area relative to sensor size
pub active_area: Option<[usize; 4]>,
// Recommended area relative to sensor size
pub crop_area: Option<[usize; 4]>,
// Hint/Replacement for EXIF BITDEPTH info
pub bps: Option<usize>,
// The BPS of the output after decoding
pub real_bps: usize,
pub highres_width: usize,
pub default_scale: DefaultScale,
pub best_quality_scale: BestQualityScale,
pub hints: Vec<String>,
pub params: HashMap<String, Value>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct DefaultScale(pub [[u32; 2]; 2]);
impl Default for DefaultScale {
fn default() -> Self {
Self([[1, 1], [1, 1]])
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct BestQualityScale(pub [u32; 2]);
impl Default for BestQualityScale {
fn default() -> Self {
Self([1, 1])
}
}
impl Camera {
pub fn find_hint(&self, hint: &str) -> bool {
self.hints.contains(&hint.to_string())
}
pub fn param_usize(&self, name: &str) -> Option<usize> {
self.params.get(name).and_then(|p| p.as_integer()).map(|i| i as usize)
}
pub fn param_i32(&self, name: &str) -> Option<i32> {
self.params.get(name).and_then(|p| p.as_integer()).map(|i| i as i32)
}
pub fn param_str(&self, name: &str) -> Option<&str> {
self.params.get(name).and_then(|p| p.as_str())
}
pub fn make_blacklevel(&self, cpp: usize) -> Option<BlackLevel> {
self.blacklevel.as_ref().map(|x| {
if x.len() == 1 {
BlackLevel::new(&vec![x[0]; cpp], 1, 1, cpp)
} else if x.len() == self.cfa.width * self.cfa.height * cpp {
BlackLevel::new(x, self.cfa.width, self.cfa.height, cpp)
} else {
panic!("Invalid blacklevel data")
}
})
}
pub fn make_whitelevel(&self, cpp: usize) -> Option<WhiteLevel> {
self.whitelevel.as_ref().map(|x| {
if x.len() == 1 {
WhiteLevel(vec![x[0] as u32; cpp])
} else if x.len() == cpp {
WhiteLevel(x.clone())
} else {
panic!("Invalid whitelevel data")
}
})
}
pub fn update_from_toml(&mut self, ct: &toml::value::Table) {
for (name, val) in ct {
match name.as_ref() {
n @ "make" => {
self.make = val.as_str().unwrap_or_else(|| panic!("{} must be a string", n)).to_string();
}
n @ "model" => {
self.model = val.as_str().unwrap_or_else(|| panic!("{} must be a string", n)).to_string();
}
n @ "mode" => {
self.mode = val.as_str().unwrap_or_else(|| panic!("{} must be a string", n)).to_string();
}
n @ "clean_make" => {
self.clean_make = val.as_str().unwrap_or_else(|| panic!("{} must be a string", n)).to_string();
}
n @ "clean_model" => {
self.clean_model = val.as_str().unwrap_or_else(|| panic!("{} must be a string", n)).to_string();
}
n @ "remark" => {
self.remark = Some(val.as_str().unwrap_or_else(|| panic!("{} must be a string", n)).to_string());
}
n @ "whitepoint" => {
let white = val.as_integer().unwrap_or_else(|| panic!("{} must be an integer", n)) as u32;
self.whitelevel = Some(vec![white]);
}
n @ "blackpoint" => {
let black = val.as_integer().unwrap_or_else(|| panic!("{} must be an integer", n)) as u32;
self.blacklevel = Some(vec![black]);
}
n @ "blackareah" => {
let vals = val.as_array().unwrap_or_else(|| panic!("{} must be an array", n));
self.blackareah = Some((vals[0].as_integer().unwrap() as usize, vals[1].as_integer().unwrap() as usize));
}
n @ "blackareav" => {
let vals = val.as_array().unwrap_or_else(|| panic!("{} must be an array", n));
self.blackareav = Some((vals[0].as_integer().unwrap() as usize, vals[1].as_integer().unwrap() as usize));
}
"color_matrix" => {
if let Some(color_matrix) = val.as_table() {
for (illu_str, matrix) in color_matrix.into_iter() {
let illu = Illuminant::new_from_str(illu_str).unwrap();
let xyz_to_cam = matrix
.as_array()
.expect("color matrix must be array")
.iter()
.map(|a| a.as_float().expect("color matrix values must be float") as f32)
.collect();
self.color_matrix.insert(illu, xyz_to_cam);
}
} else {
eprintln!("Invalid matrix spec for {}", self.clean_model);
}
assert!(!self.color_matrix.is_empty());
}
n @ "active_area" => {
let crop_vals = val.as_array().unwrap_or_else(|| panic!("{} must be an array", n));
let mut crop = [0, 0, 0, 0];
for (i, val) in crop_vals.iter().enumerate() {
crop[i] = val.as_integer().unwrap() as usize;
}
self.active_area = Some(crop);
}
n @ "crop_area" => {
let crop_vals = val.as_array().unwrap_or_else(|| panic!("{} must be an array", n));
let mut crop = [0, 0, 0, 0];
for (i, val) in crop_vals.iter().enumerate() {
crop[i] = val.as_integer().unwrap() as usize;
}
self.crop_area = Some(crop);
}
n @ "color_pattern" => {
self.cfa = CFA::new(val.as_str().unwrap_or_else(|| panic!("{} must be a string", n)));
}
n @ "plane_color" => {
self.plane_color = PlaneColor::new(val.as_str().unwrap_or_else(|| panic!("{} must be a string", n)));
}
n @ "bps" => {
self.bps = Some(val.as_integer().unwrap_or_else(|| panic!("{} must be an integer", n)) as usize);
}
n @ "real_bps" => {
self.real_bps = val.as_integer().unwrap_or_else(|| panic!("{} must be an integer", n)) as usize;
}
n @ "filesize" => {
self.filesize = val.as_integer().unwrap_or_else(|| panic!("{} must be an integer", n)) as usize;
}
n @ "raw_width" => {
self.raw_width = val.as_integer().unwrap_or_else(|| panic!("{} must be an integer", n)) as usize;
}
n @ "raw_height" => {
self.raw_height = val.as_integer().unwrap_or_else(|| panic!("{} must be an integer", n)) as usize;
}
n @ "highres_width" => {
self.highres_width = val.as_integer().unwrap_or_else(|| panic!("{} must be an integer", n)) as usize;
}
n @ "default_scale" => {
let scale_vals = val.as_array().unwrap_or_else(|| panic!("{} must be an array", n));
let scale_h = scale_vals[0].as_array().expect("must be array");
let scale_v = scale_vals[1].as_array().expect("must be array");
let scale = [
[
scale_h[0].as_integer().expect("must be integer") as u32,
scale_h[1].as_integer().expect("must be integer") as u32,
],
[
scale_v[0].as_integer().expect("must be integer") as u32,
scale_v[1].as_integer().expect("must be integer") as u32,
],
];
self.default_scale = DefaultScale(scale);
}
n @ "best_quality_scale" => {
let scale_vals = val.as_array().unwrap_or_else(|| panic!("{} must be an array", n));
self.best_quality_scale = BestQualityScale([
scale_vals[0].as_integer().expect("must be integer") as u32,
scale_vals[1].as_integer().expect("must be integer") as u32,
]);
}
n @ "hints" => {
self.hints = Vec::new();
for hint in val.as_array().unwrap_or_else(|| panic!("{} must be an array", n)) {
self.hints.push(hint.as_str().expect("hints must be a string").to_string());
}
}
n @ "params" => {
for (name, val) in val.as_table().unwrap_or_else(|| panic!("{} must be a table", n)) {
self.params.insert(name.clone(), val.clone());
}
}
"model_aliases" => {}
"modes" => {} // ignore
key => {
panic!("Unknown key: {}", key);
}
}
}
}
pub fn new() -> Camera {
Camera {
make: "".to_string(),
model: "".to_string(),
mode: "".to_string(),
clean_make: "".to_string(),
clean_model: "".to_string(),
remark: None,
filesize: 0,
raw_width: 0,
raw_height: 0,
whitelevel: None,
blacklevel: None,
blackareah: None,
blackareav: None,
xyz_to_cam: [[0.0; 3]; 4],
color_matrix: HashMap::new(),
cfa: CFA::new(""),
plane_color: PlaneColor::default(),
active_area: None,
crop_area: None,
bps: None,
real_bps: 16,
highres_width: usize::MAX,
default_scale: DefaultScale::default(),
best_quality_scale: BestQualityScale::default(),
hints: Vec::new(),
params: HashMap::new(),
//orientation: Orientation::Unknown,
}
}
}