1use std::time::Duration;
2
3use crate::{
4 coretraits::cast_u8,
5 demosaic::{run_demosaic_imageowned, Debayer, RasterMut},
6 imagetraits::ImageProps,
7 BayerError, CalcOptExp, ColorSpace, DemosaicMethod, Enlargeable, ImageRef, OptimumExposure,
8 PixelStor, PixelType, ToLuma,
9};
10use bytemuck::{AnyBitPattern, PodCastError};
11
12#[derive(Debug, PartialEq, Clone)]
31pub struct ImageOwned<T: PixelStor> {
32 pub(crate) data: Vec<T>,
33 pub(crate) width: u16,
34 pub(crate) height: u16,
35 pub(crate) channels: u8,
36 pub(crate) cspace: ColorSpace,
37}
38
39impl<T: PixelStor> ImageOwned<T> {
40 pub(crate) fn new(
41 data: Vec<T>,
42 width: usize,
43 height: usize,
44 cspace: ColorSpace,
45 ) -> Result<Self, &'static str> {
46 if height > u16::MAX as usize || width > u16::MAX as usize {
47 return Err("Image too large.");
48 }
49 if data.is_empty() {
50 return Err("Data is empty");
51 }
52 if width == 0 {
53 return Err("Width is zero");
54 }
55 if height == 0 {
56 return Err("Height is zero");
57 }
58 let channels = match cspace {
59 ColorSpace::Gray | ColorSpace::Bayer(_) => 1,
60 ColorSpace::Rgb => 3,
61 ColorSpace::Custom(ch, _) => ch as usize,
62 };
63 let len = data.len();
64 let tot = width
65 .checked_mul(height)
66 .ok_or("Image too large.")?
67 .checked_mul(channels)
68 .ok_or("Image too large.")?;
69 if tot > len {
70 return Err("Not enough data for image.");
71 }
72 let mut img = ImageOwned {
73 data,
74 width: width as u16,
75 height: height as u16,
76 channels: channels as u8,
77 cspace,
78 };
79 img.data.truncate(tot);
80 Ok(img)
81 }
82
83 pub fn from_ref(
101 data: &[T],
102 width: usize,
103 height: usize,
104 cspace: ColorSpace,
105 ) -> Result<Self, &'static str> {
106 Self::new(data.into(), width, height, cspace)
107 }
108
109 pub fn from_owned(
127 data: Vec<T>,
128 width: usize,
129 height: usize,
130 cspace: ColorSpace,
131 ) -> Result<Self, &'static str> {
132 Self::new(data, width, height, cspace)
133 }
134
135 pub fn as_slice(&self) -> &[T] {
137 self.data.as_slice()
138 }
139
140 pub fn as_mut_slice(&mut self) -> &mut [T] {
142 self.data.as_mut_slice()
143 }
144
145 pub fn into_vec(self) -> Vec<T> {
149 self.data.clone()
150 }
151
152 pub fn as_ptr(&self) -> *const T {
154 self.data.as_ptr()
155 }
156
157 pub fn as_mut_ptr(&mut self) -> *mut T {
159 self.data.as_mut_ptr()
160 }
161
162 pub fn iter(&self) -> std::slice::Iter<T> {
164 self.data.iter()
165 }
166
167 pub fn iter_mut(&mut self) -> std::slice::IterMut<T> {
169 self.data.iter_mut()
170 }
171
172 pub fn as_u8_slice(&self) -> &[u8] {
179 bytemuck::cast_slice(self.as_slice())
180 }
181
182 pub fn as_u8_slice_checked(&self) -> Option<&[u8]> {
184 bytemuck::try_cast_slice(self.as_slice()).ok()
185 }
186}
187
188impl<T: PixelStor> ImageProps for ImageOwned<T> {
189 type OutputU8 = ImageOwned<u8>;
190
191 fn width(&self) -> usize {
192 self.width as usize
193 }
194
195 fn height(&self) -> usize {
196 self.height as usize
197 }
198
199 fn channels(&self) -> u8 {
200 self.channels
201 }
202
203 fn color_space(&self) -> ColorSpace {
204 self.cspace.clone()
205 }
206
207 fn pixel_type(&self) -> PixelType {
208 T::PIXEL_TYPE
209 }
210
211 fn len(&self) -> usize {
212 self.data.len()
213 }
214
215 fn is_empty(&self) -> bool {
216 self.data.is_empty()
217 }
218
219 fn cast_u8(&self) -> Self::OutputU8 {
220 let out = cast_u8(self.data.as_slice());
221 Self::OutputU8 {
222 data: out,
223 width: self.width() as _,
224 height: self.height() as _,
225 cspace: self.cspace.clone(),
226 channels: self.channels(),
227 }
228 }
229}
230
231impl<T: PixelStor + AnyBitPattern> ImageOwned<T> {
232 pub fn from_u8(
261 data: &[u8],
262 width: usize,
263 height: usize,
264 cspace: ColorSpace,
265 ) -> Result<Self, &'static str> {
266 let data = bytemuck::try_cast_slice(data).map_err(|e| {
267 use PodCastError::*;
268 match e {
269 TargetAlignmentGreaterAndInputNotAligned => {
270 "Target alignment greater and input not aligned"
271 }
272 OutputSliceWouldHaveSlop => "Output slice would have slop",
273 SizeMismatch => "Size mismatch",
274 AlignmentMismatch => "Alignment mismatch",
275 }
276 })?;
277 Self::from_ref(data, width, height, cspace)
278 }
279}
280
281impl<T: PixelStor + Enlargeable> ToLuma for ImageOwned<T> {
282 fn to_luma(&mut self) -> Result<(), &'static str> {
283 self.to_luma_custom(&[0.299, 0.587, 0.114])
284 }
285
286 fn to_luma_custom(&mut self, coeffs: &[f64]) -> Result<(), &'static str> {
287 match self.cspace {
289 ColorSpace::Gray => Err("Image is already grayscale."),
290 ColorSpace::Rgb | ColorSpace::Custom(_, _) => {
291 crate::coreimpls::run_luma(
292 self.channels.into(),
293 self.data.len(),
294 self.data.as_mut_slice(),
295 coeffs,
296 )?;
297 self.cspace = ColorSpace::Gray;
298 let len = self.width as usize * self.height as usize;
299 self.channels = 1;
300 self.data.truncate(len);
301 Ok(())
302 }
303 ColorSpace::Bayer(_) => Err("Image is not debayered."),
304 }
305 }
306}
307
308impl<T: PixelStor + Enlargeable> Debayer for ImageOwned<T> {
309 type Output = ImageOwned<T>;
310 fn debayer(&self, alg: DemosaicMethod) -> Result<Self::Output, BayerError> {
311 let cfa = self
312 .cspace
313 .clone()
314 .try_into()
315 .map_err(BayerError::InvalidColorSpace)?;
316 if self.channels != 1 {
317 return Err(BayerError::WrongDepth);
318 }
319 let mut dst = vec![T::zero(); self.width() * self.height() * 3];
320 let mut raster = RasterMut::new(self.width(), self.height(), &mut dst);
321 run_demosaic_imageowned(self, cfa, alg, &mut raster)?;
322 Ok(Self::Output {
323 data: dst,
324 width: self.width,
325 height: self.height,
326 channels: 3,
327 cspace: ColorSpace::Rgb,
328 })
329 }
330}
331
332impl<'a, T: PixelStor> From<&ImageRef<'a, T>> for ImageOwned<T> {
333 fn from(data: &ImageRef<'a, T>) -> Self {
334 Self {
335 data: data.data.to_vec(),
336 width: data.width,
337 height: data.height,
338 channels: data.channels,
339 cspace: data.cspace.clone(),
340 }
341 }
342}
343
344impl<T: PixelStor + Ord> CalcOptExp for ImageOwned<T> {
345 fn calc_opt_exp(
346 mut self,
347 eval: &OptimumExposure,
348 exposure: Duration,
349 bin: u8,
350 ) -> Result<(Duration, u16), &'static str> {
351 let len = self.data.len();
352 eval.calculate(self.data.as_mut_slice(), len, exposure, bin)
353 }
354}
355
356mod test {
357
358 #[test]
359 fn test_into_luma() {
360 use crate::{ColorSpace, ImageOwned, ToLuma};
361 let data = vec![
362 181u8, 178, 118, 183, 85, 131, 82, 143, 196, 108, 64, 33, 174, 43, 18, 236, 19, 179,
363 178, 132, 14, 32, 82, 1, 185, 221, 160, 112, 67, 179, 248, 104, 31, 105, 33, 100, 73,
364 108, 241, 108, 208, 44, 138, 91, 188, 251, 132, 25, 233, 5, 51, 189, 41, 39, 62, 236,
365 71, 150, 85, 11, 46, 95, 108, 228, 36, 187, 144, 203, 34, 218, 116, 207, 111, 168, 181,
366 172, 186, 245, 223, 187, 203, 64, 70, 160, 23, 112, 11, 149, 76, 182, 206, 203, 137,
367 60, 83, 94, 103, 91, 146, 176, 186, 244, 59, 144, 171, 120, 79, 144, 143, 184, 41, 137,
368 4, 141, 70, 167, 51, 212, 39, 219, 102, 206, 124, 10, 92, 159, 193, 115, 132, 156, 58,
369 1, 41, 89, 145, 111, 225, 177, 233, 18, 221, 20, 199, 34, 2, 189, 214, 101, 170, 33,
370 223, 95, 127, 106, 169, 198, 195, 23, 29, 202, 68, 31, 127, 210, 77, 229, 204, 132, 45,
371 70, 241, 160, 14, 25, 125, 10, 25, 171, 1, 13, 212, 188, 143, 139, 13, 138, 17, 128,
372 226, 78, 84, 212, 230, 201, 22, 27, 189, 225, 141, 115, 64, 99, 103, 109, 173, 234,
373 115, 172, 169, 208, 137, 203, 59, 108, 52, 160, 102, 185, 186, 251, 23, 185, 242, 219,
374 195, 242, 75, 202, 153, 198, 102, 103, 151, 228, 211, 57, 178, 26, 254, 38, 47, 189,
375 118, 246, 184, 104, 195, 40, 108, 155, 158, 47, 27, 138, 212, 61, 113, 24, 111, 171,
376 47, 0, 57, 91, 213, 155, 254, 241, 58, 60, 204, 235, 37, 130, 6, 125, 185, 64, 228,
377 242, 117, 52, 215, 126, 115, 50, 147, 203, 220, 192, 175, 137, 40, 191, 17, 191, 122,
378 136, 168, 215, 220, 153, 179, 123, 189, 1, 45, 68, 108, 234, 98, 236, 178, 32, 141, 5,
379 46, 191, 1, 81, 169, 48, 138, 89, 208, 88, 217, 183, 105, 87, 94, 53, 125, 6, 86, 201,
380 11, 65, 227, 101, 221, 47, 97, 15, 192, 191, 231, 199, 119, 47, 24, 44, 33, 207, 100,
381 147, 116, 60, 104, 215, 36, 95, 61, 133, 4, 89, 71, 0, 98, 82, 210, 179, 193, 29, 59,
382 148, 209, 172, 231, 206, 46, 103, 106, 37, 128, 104, 201, 143, 249, 251, 18, 92, 114,
383 92, 211, 129, 153, 168, 90, 133, 78, 254, 169, 125, 36, 26, 190, 126, 212, 77, 219,
384 163, 61, 46, 79, 167, 50, 49, 126, 154, 105, 21, 212, 92, 5, 125, 163, 84, 35, 40, 150,
385 121, 127, 37, 149, 240, 75, 56, 81, 79, 163, 153, 182, 123, 17, 64, 57, 134, 162, 179,
386 148, 228, 179, 71, 15, 116, 249, 39, 15, 39, 2, 171, 103, 64, 19, 192, 101, 235, 119,
387 241, 181, 117, 118, 68, 137, 33, 88, 203, 30, 127, 126, 62, 182, 247, 10, 96, 77, 109,
388 183, 223, 129, 216, 76, 141, 43, 232, 169, 100, 147, 196, 182, 155, 196, 50, 211, 252,
389 220, 231, 60, 252, 64, 230, 193, 29, 217, 164, 137, 113, 149, 93, 20, 86, 10, 220, 54,
390 161, 198, 119, 231, 235, 89, 23, 88, 167, 116, 133, 74, 244, 64, 1, 131, 106, 130, 44,
391 248, 152, 79, 82, 237, 113, 137, 228, 17, 31, 244, 28, 38, 32, 69, 215, 215, 81, 12,
392 215, 172, 73, 199, 219, 74, 103, 244, 217, 171, 60, 50, 252, 147, 100, 26, 28, 72, 162,
393 215, 136, 192, 166, 178, 108, 194, 48, 37, 153, 51, 10, 169, 238, 173, 209, 189, 133,
394 164, 93, 111, 156, 129, 171, 54, 157, 13, 46, 9, 201, 23, 234, 87, 175, 168, 133, 230,
395 114, 90, 214, 240, 69, 90, 27, 199, 158, 150, 100, 94, 204, 35, 103, 216, 120, 122, 43,
396 117, 204, 59, 88, 185, 128, 161, 87, 71, 179, 154, 39, 7, 183, 17, 138, 95, 178, 133,
397 196, 249, 210, 68, 64, 230, 250, 181, 230, 34, 101, 154, 247, 171, 254, 254, 205, 147,
398 54, 250, 48, 174, 237, 81, 201, 170, 28, 166, 185, 52, 57, 128, 110, 64, 64, 64, 204,
399 58, 73, 55, 101, 94, 180, 232, 172, 126, 45, 242, 185, 49, 146, 203, 152, 198, 176,
400 174, 44, 17, 26, 140, 117, 32, 186, 233, 213, 8, 135, 199, 218, 5, 16, 114, 170, 13,
401 91, 171, 247, 88, 158, 95, 220, 127, 126, 12, 3, 124, 198, 134, 151, 21, 98, 200, 157,
402 131, 82, 216, 142, 218, 19, 142, 73, 108, 155, 51, 254, 221, 41, 85, 57, 60, 176,
403 ];
404 let mut img = ImageOwned::from_owned(data, 16, 16, ColorSpace::Rgb).unwrap();
405 img.to_luma().unwrap();
406 let expected = vec![
407 172, 119, 130, 73, 79, 102, 132, 57, 203, 93, 138, 62, 112, 159, 116, 155, 78, 85, 165,
408 95, 81, 110, 166, 156, 152, 188, 199, 78, 73, 109, 196, 77, 100, 189, 121, 98, 155, 59,
409 124, 111, 165, 75, 140, 80, 81, 185, 105, 126, 135, 133, 136, 153, 75, 103, 170, 203,
410 82, 58, 46, 53, 190, 64, 105, 96, 189, 144, 116, 102, 202, 174, 166, 81, 160, 109, 223,
411 139, 173, 145, 116, 161, 138, 193, 94, 144, 113, 87, 138, 43, 183, 112, 203, 56, 118,
412 146, 151, 124, 198, 86, 131, 163, 175, 147, 65, 154, 88, 50, 67, 105, 138, 126, 73, 75,
413 67, 165, 59, 215, 65, 56, 129, 103, 73, 52, 32, 168, 81, 186, 195, 97, 122, 217, 72,
414 166, 154, 114, 128, 133, 133, 89, 127, 106, 67, 44, 102, 113, 76, 122, 89, 166, 49,
415 155, 198, 43, 99, 32, 70, 143, 197, 112, 70, 92, 94, 90, 107, 167, 110, 179, 179, 167,
416 236, 133, 176, 154, 124, 49, 138, 177, 217, 77, 121, 110, 116, 176, 98, 140, 51, 34,
417 171, 55, 116, 120, 219, 76, 105, 69, 166, 166, 90, 76, 209, 188, 116, 141, 109, 41,
418 154, 166, 145, 212, 65, 146, 151, 171, 75, 105, 148, 88, 69, 80, 148, 228, 84, 207, 87,
419 203, 213, 168, 200, 163, 164, 104, 63, 103, 86, 209, 91, 100, 172, 159, 36, 74, 195,
420 182, 23, 68, 206, 128, 113, 96, 131, 164, 111, 172, 97, 105, 99, 72,
421 ];
422 assert_eq!(img.as_slice(), &expected[..]);
423 }
424
425 #[test]
426 fn test_u8_src() {
427 let mut data = vec![181u16, 178, 118, 183, 85, 131];
428 let img =
429 crate::ImageOwned::from_owned(data.clone(), 3, 2, crate::ColorSpace::Gray).unwrap();
430 let data = bytemuck::cast_slice_mut(&mut data);
431 let img2 = crate::ImageOwned::<u16>::from_u8(data, 3, 2, crate::ColorSpace::Gray).unwrap();
432 assert_eq!(img.as_slice(), img2.as_slice());
433 }
434
435 #[test]
436 fn test_optimum_exposure() {
437 use crate::CalcOptExp;
438 let opt_exp = crate::OptimumExposureBuilder::default()
439 .pixel_exclusion(1)
440 .build()
441 .unwrap();
442 let img = vec![0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9];
443 let img = crate::ImageOwned::from_owned(img, 5, 2, crate::ColorSpace::Gray)
444 .expect("Failed to create ImageOwned");
445 let exp = std::time::Duration::from_secs(10); let bin = 1; let res = img.calc_opt_exp(&opt_exp, exp, bin).unwrap();
448 assert_eq!(res, (exp, bin as u16));
449 }
450}