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}