1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
use std::fmt;
use crate::decoders::tiff::*;
/// Representation of the color filter array pattern in raw cameras
///
/// # Example
/// ```
/// use rawloader::CFA;
/// let cfa = CFA::new("RGGB");
/// assert_eq!(cfa.color_at(0,0), 0);
/// assert_eq!(cfa.color_at(0,1), 1);
/// assert_eq!(cfa.color_at(1,0), 1);
/// assert_eq!(cfa.color_at(1,1), 2);
/// ```
///
/// You will almost always get your CFA struct from a RawImage decode, already fully
/// initialized and ready to be used in processing. The color_at() implementation is
/// designed to be fast so it can be called inside the inner loop of demosaic or other
/// color-aware algorithms that work on pre-demosaic data
#[derive(Clone)]
pub struct CFA {
/// CFA pattern as a String
pub name: String,
/// Width of the repeating pattern
pub width: usize,
/// Height of the repeating pattern
pub height: usize,
pattern: [[usize;48];48],
}
impl CFA {
#[doc(hidden)] pub fn new_from_tag(pat: &TiffEntry) -> CFA {
let mut patname = String::new();
for i in 0..pat.count() {
patname.push(match pat.get_u32(i as usize) {
0 => 'R',
1 => 'G',
2 => 'B',
_ => 'U',
});
}
CFA::new(&patname)
}
/// Create a new CFA from a string describing it. For simplicity the pattern is specified
/// as each pixel being one of R/G/B/E representing the 0/1/2/3 colors in a 4 color image.
/// The pattern is specified as the colors in each row concatenated so RGGB means that
/// the first row is RG and the second row GB. Row size is determined by pattern size
/// (e.g., the xtrans pattern is 6x6 and thus 36 characters long). In theory this could
/// lead to confusion between different pattern sizes but in practice there are only
/// a few oddball cameras no one cares about that do anything but 2x2 and 6x6 (and those
/// work fine with this as well).
pub fn new(patname: &str) -> CFA {
let (width, height) = match patname.len() {
0 => (0,0),
4 => (2,2),
36 => (6,6),
16 => (2,8),
144 => (12,12),
_ => panic!("Unknown CFA size \"{}\"", patname),
};
let mut pattern: [[usize;48];48] = [[0;48];48];
if width > 0 {
// copy the pattern into the top left
for (i,c) in patname.bytes().enumerate() {
pattern[i/width][i%width] = match c {
b'R' => 0,
b'G' => 1,
b'B' => 2,
b'E' => 3,
b'M' => 1,
b'Y' => 3,
_ => {
let unknown_char = patname[i..].chars().next().unwrap();
panic!("Unknown CFA color \"{}\" in pattern \"{}\"", unknown_char, patname)
},
};
}
// extend the pattern into the full matrix
for row in 0..48 {
for col in 0..48 {
pattern[row][col] = pattern[row%height][col%width];
}
}
}
CFA {
name: patname.to_string(),
pattern: pattern,
width: width,
height: height,
}
}
/// Get the color index at the given position. Designed to be fast so it can be called
/// from inner loops without performance issues.
pub fn color_at(&self, row: usize, col: usize) -> usize {
self.pattern[(row+48) % 48][(col+48) % 48]
}
/// Shift the pattern left and/or down. This is useful when cropping the image to get
/// the equivalent pattern of the crop when it's not a multiple of the pattern size.
///
/// # Example
/// ```
/// use rawloader::CFA;
/// let cfa = CFA::new("RGGB");
/// assert_eq!(cfa.color_at(0,0), 0);
/// assert_eq!(cfa.color_at(0,1), 1);
/// assert_eq!(cfa.color_at(1,0), 1);
/// assert_eq!(cfa.color_at(1,1), 2);
///
/// let shifted = cfa.shift(1,1);
/// assert_eq!(shifted.color_at(0,0), 2);
/// assert_eq!(shifted.color_at(0,1), 1);
/// assert_eq!(shifted.color_at(1,0), 1);
/// assert_eq!(shifted.color_at(1,1), 0);
/// ```
pub fn shift(&self, x: usize, y: usize) -> CFA {
let mut pattern: [[usize;48];48] = [[0;48];48];
for row in 0..48 {
for col in 0..48 {
pattern[row][col] = self.color_at(row+y,col+x);
}
}
let mut name = "".to_string();
for row in 0..self.height {
for col in 0..self.width {
name.push_str(match pattern[row][col] {
0 => "R",
1 => "G",
2 => "B",
3 => "E",
x => panic!("Unknown CFA color \"{}\"", x),
});
}
}
CFA {
name: name,
pattern: pattern,
width: self.width,
height: self.height,
}
}
/// Test if this is actually a valid CFA pattern
///
/// # Example
/// ```
/// use rawloader::CFA;
/// let cfa = CFA::new("RGGB");
/// assert!(cfa.is_valid());
///
/// let cfa = CFA::new("");
/// assert!(!cfa.is_valid());
/// ```
pub fn is_valid(&self) -> bool {
self.width != 0 && self.height != 0
}
/// Convert the CFA back into a pattern string
///
/// # Example
/// ```
/// use rawloader::CFA;
/// let cfa = CFA::new("RGGB");
/// assert_eq!(cfa.to_string(), "RGGB");
///
/// let shifted = cfa.shift(1,1);
/// assert_eq!(shifted.to_string(), "BGGR");
/// ```
pub fn to_string(&self) -> String {
self.name.clone()
}
}
impl fmt::Debug for CFA {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "CFA {{ {} }}", self.name)
}
}