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