1use std::cmp;
6use std::fmt::Display;
7use std::ops::RangeInclusive;
8use crate::FeagiDataError;
9use crate::basic_components::{CartesianResolution, FlatCoordinateU32};
10use crate::data::{ImageFrame, SegmentedImageFrame};
11
12#[repr(transparent)]
16#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
17pub struct ImageXYPoint(FlatCoordinateU32);
18
19impl ImageXYPoint {
20 pub fn new(x: u32, y: u32) -> Self {
21 ImageXYPoint(FlatCoordinateU32::new(x, y))
22 }
23}
24
25impl std::ops::Deref for ImageXYPoint {
26 type Target = FlatCoordinateU32;
27 fn deref(&self) -> &Self::Target {
28 &self.0
29 }
30}
31
32impl From<FlatCoordinateU32> for ImageXYPoint {
33 fn from(x: FlatCoordinateU32) -> Self {
34 ImageXYPoint(x)
35 }
36}
37
38impl From<ImageXYPoint> for FlatCoordinateU32 {
39 fn from(x: ImageXYPoint) -> Self {
40 x.0
41 }
42}
43
44impl Display for ImageXYPoint {
45 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
46 write!(f, "[{}, {}]", self.0, self.0)
47 }
48}
49
50
51
52#[repr(transparent)]
54#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
55pub struct ImageXYResolution(CartesianResolution);
56
57impl ImageXYResolution {
58 pub fn new(x_width: usize, y_height: usize,) -> Result<Self,FeagiDataError> {
59 Ok(ImageXYResolution(CartesianResolution::new(x_width, y_height)?))
60 }
61}
62
63impl std::ops::Deref for ImageXYResolution {
64 type Target = CartesianResolution;
65 fn deref(&self) -> &Self::Target {
66 &self.0
67 }
68}
69
70impl From<CartesianResolution> for ImageXYResolution {
71 fn from(coord: CartesianResolution) -> Self {
72 ImageXYResolution(coord)
73 }
74}
75
76impl From<ImageXYResolution> for CartesianResolution {
77 fn from(coord: ImageXYResolution) -> Self {
78 coord.0
79 }
80}
81
82impl Display for ImageXYResolution {
83 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
84 write!(f, "<{}w by {}h>", self.0, self.0)
85 }
86}
87
88
89#[derive(PartialEq, Clone, Copy, Debug, Eq, Hash)]
97pub struct SegmentedXYImageResolutions {
98 pub lower_left: ImageXYResolution,
99 pub lower_middle: ImageXYResolution,
100 pub lower_right: ImageXYResolution,
101 pub middle_left: ImageXYResolution,
102 pub center: ImageXYResolution,
103 pub middle_right: ImageXYResolution,
104 pub upper_left: ImageXYResolution,
105 pub upper_middle: ImageXYResolution,
106 pub upper_right: ImageXYResolution,
107}
108
109impl SegmentedXYImageResolutions {
110
111 pub fn new(
112 lower_left: ImageXYResolution,
113 lower_middle: ImageXYResolution,
114 lower_right: ImageXYResolution,
115 middle_left: ImageXYResolution,
116 center: ImageXYResolution,
117 middle_right: ImageXYResolution,
118 upper_left: ImageXYResolution,
119 upper_middle: ImageXYResolution,
120 upper_right: ImageXYResolution,
121 ) -> SegmentedXYImageResolutions {
122 SegmentedXYImageResolutions {
123 lower_left,
124 lower_middle,
125 lower_right,
126 middle_left,
127 center,
128 middle_right,
129 upper_left,
130 upper_middle,
131 upper_right,
132 }
133 }
134
135 pub fn create_with_same_sized_peripheral(center_resolution: ImageXYResolution, peripheral_resolutions: ImageXYResolution) -> SegmentedXYImageResolutions {
151
152 SegmentedXYImageResolutions::new(peripheral_resolutions, peripheral_resolutions,
153 peripheral_resolutions, peripheral_resolutions,
154 center_resolution, peripheral_resolutions,
155 peripheral_resolutions, peripheral_resolutions,
156 peripheral_resolutions)
157 }
158
159 pub fn as_ordered_array(&self) ->[&ImageXYResolution; 9] {
160 [
161 &self.lower_left,
162 &self.lower_middle,
163 &self.lower_right,
164 &self.middle_left,
165 &self.center,
166 &self.middle_right,
167 &self.upper_left,
168 &self.upper_middle,
169 &self.upper_right,
170 ]
171 }
172}
173
174impl Display for SegmentedXYImageResolutions {
175 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
176 write!(f, "LowerLeft:{}, LowerMiddle:{}, LowerRight:{}, MiddleLeft:{}, Center:{}, MiddleRight:{}, TopLeft:{}, TopMiddle:{}, TopRight:{}",
177 self.lower_left, self.lower_middle, self.lower_right, self.middle_left, self.center, self.middle_right, self.upper_left, self.upper_middle, self.upper_right)
178 }
179}
180
181#[derive(Debug, PartialEq, Clone, Copy, Eq, Hash)]
191pub enum ColorSpace {
192 Linear,
193 Gamma
194}
195
196impl std::fmt::Display for ColorSpace {
197 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
198 match self {
199 ColorSpace::Linear => write!(f, "Linear"),
200 ColorSpace::Gamma => write!(f, "Gamma"),
201 }
202 }
203}
204
205#[derive(Debug, PartialEq, Clone, Copy, Eq, Hash)]
213pub enum ColorChannelLayout {
214 GrayScale = 1, RG = 2,
216 RGB = 3,
217 RGBA = 4,
218}
219
220impl Display for ColorChannelLayout {
221 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
222 match self {
223 ColorChannelLayout::GrayScale => write!(f, "ChannelLayout(GrayScale)"),
224 ColorChannelLayout::RG => write!(f, "ChannelLayout(RedGreen)"),
225 ColorChannelLayout::RGB => write!(f, "ChannelLayout(RedGreenBlue)"),
226 ColorChannelLayout::RGBA => write!(f, "ChannelLayout(RedGreenBlueAlpha)"),
227 }
228 }
229}
230
231impl TryFrom<usize> for ColorChannelLayout {
232 type Error = FeagiDataError;
233 fn try_from(value: usize) -> Result<Self, Self::Error> {
234 match value {
235 1 => Ok(ColorChannelLayout::GrayScale),
236 2 => Ok(ColorChannelLayout::RG),
237 3 => Ok(ColorChannelLayout::RGB),
238 4 => Ok(ColorChannelLayout::RGBA),
239 _ => Err(FeagiDataError::BadParameters(format!("No Channel Layout has {} channels! Acceptable values are 1,2,3,4!", value)).into())
240 }
241 }
242}
243
244impl TryFrom<image::ColorType> for ColorChannelLayout {
245 type Error = FeagiDataError;
246 fn try_from(value: image::ColorType) -> Result<Self, Self::Error> {
247 match value {
248 image::ColorType::L8 => Ok(ColorChannelLayout::GrayScale),
249 image::ColorType::La8 => Ok(ColorChannelLayout::RG),
250 image::ColorType::Rgb8 => Ok(ColorChannelLayout::RGB),
251 image::ColorType::Rgba8 => Ok(ColorChannelLayout::RGBA),
252 _ => Err(FeagiDataError::BadParameters("Unsupported image color!".to_string()))
253 }
254 }
255}
256
257impl From<ColorChannelLayout> for usize {
258 fn from(value: ColorChannelLayout) -> usize {
259 value as usize
260 }
261}
262
263#[derive(Debug, PartialEq, Clone, Copy)]
273pub enum MemoryOrderLayout {
274 HeightsWidthsChannels, ChannelsHeightsWidths, WidthsHeightsChannels, HeightsChannelsWidths,
278 ChannelsWidthsHeights,
279 WidthsChannelsHeights,
280}
281
282impl Display for MemoryOrderLayout {
283 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
284 match self {
285 MemoryOrderLayout::HeightsWidthsChannels => write!(f, "HeightsWidthsChannels"),
286 MemoryOrderLayout::ChannelsHeightsWidths => write!(f, "ChannelsHeightsWidths"),
287 MemoryOrderLayout::WidthsHeightsChannels => write!(f, "WidthsHeightsChannels"),
288 MemoryOrderLayout::HeightsChannelsWidths => write!(f, "HeightsChannelsWidths"),
289 MemoryOrderLayout::ChannelsWidthsHeights => write!(f, "ChannelsWidthsHeights"),
290 MemoryOrderLayout::WidthsChannelsHeights => write!(f, "WidthsChannelsHeights"),
291 }
292 }
293}
294#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
299pub struct ImageFrameProperties {
300 image_resolution: ImageXYResolution,
301 color_space: ColorSpace,
302 color_channel_layout: ColorChannelLayout,
303}
304
305impl ImageFrameProperties {
306 pub fn new(image_resolution: ImageXYResolution, color_space: ColorSpace, color_channel_layout: ColorChannelLayout) -> Result<Self, FeagiDataError> {
318 Ok(ImageFrameProperties{
319 image_resolution,
320 color_space,
321 color_channel_layout,
322 })
323 }
324
325 pub fn verify_image_frame_matches_properties(&self, image_frame: &ImageFrame) -> Result<(), FeagiDataError> {
346 if image_frame.get_xy_resolution() != self.image_resolution {
347 return Err(FeagiDataError::BadParameters(format!{"Expected resolution of {} but received an image with resolution of {}!",
348 self.image_resolution, image_frame.get_xy_resolution()}).into())
349 }
350 if image_frame.get_color_space() != &self.color_space {
351 return Err(FeagiDataError::BadParameters(format!("Expected color space of {}, but got image with color space of {}!", self.color_space.to_string(), self.color_space.to_string())).into())
352 }
353 if image_frame.get_channel_layout() != &self.color_channel_layout {
354 return Err(FeagiDataError::BadParameters(format!("Expected color channel layout of {}, but got image with color channel layout of {}!", self.color_channel_layout.to_string(), self.color_channel_layout.to_string())).into())
355 }
356 Ok(())
357 }
358
359 pub fn get_image_resolution(&self) -> ImageXYResolution {
365 self.image_resolution
366 }
367
368 pub fn get_color_space(&self) -> ColorSpace {
374 self.color_space
375 }
376
377 pub fn get_color_channel_layout(&self) -> ColorChannelLayout {
383 self.color_channel_layout
384 }
385
386 pub fn get_number_of_channels(&self) -> usize {
387 self.color_channel_layout.into()
388 }
389
390 pub fn get_number_of_samples(&self) -> usize {
391 self.image_resolution.width * self.image_resolution.height * self.get_number_of_channels()
392 }
393}
394
395impl Display for ImageFrameProperties {
396 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
397 let s = format!("ImageFrameProperties({}, {}, {})", self.image_resolution, self.color_space.to_string(), self.color_channel_layout.to_string());
398 write!(f, "{}", s)
399 }
400}
401
402#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
407pub struct SegmentedImageFrameProperties {
408 segment_xy_resolutions: SegmentedXYImageResolutions,
409 center_color_channel: ColorChannelLayout,
410 peripheral_color_channels: ColorChannelLayout,
411 color_space: ColorSpace,
412}
413impl SegmentedImageFrameProperties {
414 pub fn new( segment_xy_resolutions: &SegmentedXYImageResolutions,
416 center_color_channels: &ColorChannelLayout,
417 peripheral_color_channels: &ColorChannelLayout,
418 color_space: &ColorSpace,
419 ) -> SegmentedImageFrameProperties {
420 SegmentedImageFrameProperties {
421 segment_xy_resolutions: segment_xy_resolutions.clone(),
422 center_color_channel: center_color_channels.clone(),
423 peripheral_color_channels: peripheral_color_channels.clone(),
424 color_space: *color_space,
425 }
426 }
427
428 pub fn get_resolutions(&self) -> &SegmentedXYImageResolutions {
429 &self.segment_xy_resolutions
430 }
431
432 pub fn get_center_color_channel(&self) -> &ColorChannelLayout {
433 &self.center_color_channel
434 }
435
436 pub fn get_peripheral_color_channels(&self) -> &ColorChannelLayout {
437 &self.peripheral_color_channels
438 }
439
440 pub fn get_color_space(&self) -> &ColorSpace {
441 &self.color_space
442 }
443
444 pub fn verify_segmented_image_frame_matches_properties(&self, segmented_image_frame: &SegmentedImageFrame) -> Result<(), FeagiDataError> {
445 if self != &segmented_image_frame.get_segmented_image_frame_properties() {
446 return Err(FeagiDataError::BadParameters("Segmented image frame does not match the expected segmented frame properties!".into()).into())
447 }
448 Ok(())
449 }
450
451
452}
453
454impl Display for SegmentedImageFrameProperties {
455 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
456 write!(f, "SegmentedImageFrameProperties(TODO)") }
458}
459
460#[derive(Debug, PartialEq, Clone, Copy)]
465pub struct CornerPoints {
466 pub upper_left: ImageXYPoint,
467 pub lower_right: ImageXYPoint,
468
469}
470
471impl CornerPoints {
472
473 pub fn new(upper_left: ImageXYPoint, lower_right: ImageXYPoint) -> Result<Self, FeagiDataError> {
474 if lower_right.x <= upper_left.x || lower_right.y <= upper_left.y {
475 return Err(FeagiDataError::BadParameters("Given Points are not forming a proper rectangle!".into()).into())
476 }
477 Ok(CornerPoints {
478 upper_left, lower_right
479 })
480 }
481 pub fn get_upper_right(&self) -> ImageXYPoint {
482 ImageXYPoint::new(self.lower_right.x, self.upper_left.y)
483 }
484
485 pub fn get_lower_left(&self) -> ImageXYPoint {
486 ImageXYPoint::new(self.upper_left.x, self.lower_right.y)
487 }
488
489 pub fn get_width(&self) -> u32 {
490 self.lower_right.x - self.upper_left.x
491 }
492
493 pub fn get_height(&self) -> u32 {
494 self.lower_right.y - self.upper_left.y
495 }
496
497 pub fn enclosed_area_width_height(&self) -> ImageXYResolution {
498 ImageXYResolution::new(self.get_width() as usize, self.get_height() as usize).unwrap()
499 }
500
501 pub fn verify_fits_in_resolution(&self, resolution: ImageXYResolution) -> Result<(), FeagiDataError> {
502 if self.lower_right.x > resolution.width as u32 || self.lower_right.y > resolution.height as u32 {
503 return Err(FeagiDataError::BadParameters(format!("Corner Points {} do not fit in given resolution {}!", self, resolution)).into())
504 }
505 Ok(())
506 }
507}
508
509impl Display for CornerPoints {
510 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
511 write!(f, "CornerPoints(Upper Left: {}, Lower Right: {})", self.upper_left.to_string(), self.lower_right.to_string())
512 }
513}
514
515#[derive(PartialEq, Clone, Copy, Debug)]
523pub struct GazeProperties {
524 pub(crate) eccentricity_normalized_xy: (f32, f32), pub(crate) modularity_normalized_xy: (f32, f32), }
529
530impl GazeProperties {
531
532 pub fn new(eccentricity_center_xy: (f32, f32), modularity_size_xy: (f32, f32)) -> Self {
533 GazeProperties {
534 eccentricity_normalized_xy: (eccentricity_center_xy.0.clamp(0.0, 1.0), eccentricity_center_xy.1.clamp(0.0, 1.0)),
535 modularity_normalized_xy: (modularity_size_xy.0.clamp(0.0, 1.0), modularity_size_xy.1.clamp(0.0, 1.0)),
536 }
537 }
538
539 pub fn create_default_centered() -> GazeProperties {
548 GazeProperties::new((0.5, 0.5), (0.5, 0.5))
549 }
550
551 pub fn calculate_source_corner_points_for_segmented_video_frame(&self, source_frame_resolution: ImageXYResolution) -> Result<[CornerPoints; 9], FeagiDataError> {
552 if source_frame_resolution.width < 3 || source_frame_resolution.height < 3 {
553 return Err(FeagiDataError::BadParameters("Source frame width and height must be at least 3!".into()).into())
554 }
555
556
557 let center_corner_points = self.calculate_pixel_coordinates_of_center_corners(source_frame_resolution)?;
558 Ok([
559 CornerPoints::new(ImageXYPoint::new(0, center_corner_points.lower_right.y), ImageXYPoint::new(center_corner_points.upper_left.x, source_frame_resolution.height as u32))?,
560 CornerPoints::new(center_corner_points.get_lower_left(), ImageXYPoint::new(center_corner_points.lower_right.x, source_frame_resolution.height as u32))?,
561 CornerPoints::new(center_corner_points.lower_right, ImageXYPoint::new(source_frame_resolution.width as u32, source_frame_resolution.height as u32))?,
562 CornerPoints::new(ImageXYPoint::new(0, center_corner_points.upper_left.y), center_corner_points.get_lower_left())?,
563 center_corner_points,
564 CornerPoints::new(center_corner_points.get_upper_right(), ImageXYPoint::new(source_frame_resolution.width as u32, center_corner_points.lower_right.y))?,
565 CornerPoints::new(ImageXYPoint::new(0,0), center_corner_points.upper_left)?,
566 CornerPoints::new(ImageXYPoint::new(center_corner_points.upper_left.x, 0), center_corner_points.get_upper_right())?,
567 CornerPoints::new(ImageXYPoint::new(center_corner_points.lower_right.x, 0), ImageXYPoint::new(source_frame_resolution.width as u32, center_corner_points.upper_left.y))?,
568 ])
569 }
570
571 fn calculate_pixel_coordinates_of_center_corners(&self, source_frame_resolution: ImageXYResolution) -> Result<CornerPoints, FeagiDataError> {
572 let source_frame_width_height_f: (f32, f32) = (source_frame_resolution.width as f32, source_frame_resolution.height as f32);
573 let center_size_normalized_half_xy: (f32, f32) = (self.modularity_normalized_xy.0 / 2.0, self.modularity_normalized_xy.1 / 2.0);
574
575 let bottom_pixel: usize = cmp::min(source_frame_resolution.height - 1,
577 ((self.eccentricity_normalized_xy.1 + center_size_normalized_half_xy.1) * source_frame_width_height_f.1).floor() as usize);
578 let top_pixel: usize = cmp::max(1,
579 (( self.eccentricity_normalized_xy.1 - center_size_normalized_half_xy.1) * source_frame_width_height_f.1).floor() as usize);
580 let left_pixel: usize = cmp::max(1,
581 ((self.eccentricity_normalized_xy.0 - center_size_normalized_half_xy.0) * source_frame_width_height_f.0).floor() as usize);
582 let right_pixel: usize = cmp::min(source_frame_resolution.width - 1,
583 (( self.eccentricity_normalized_xy.0 + center_size_normalized_half_xy.0) * source_frame_width_height_f.0).floor() as usize);
584
585 let top_left = ImageXYPoint::new(left_pixel as u32, top_pixel as u32);
586 let bottom_right = ImageXYPoint::new(right_pixel as u32, bottom_pixel as u32);
587
588 let corner_points: CornerPoints = CornerPoints::new(top_left, bottom_right)?;
589 Ok(corner_points)
590 }
591}
592
593impl std::fmt::Display for GazeProperties {
594 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
595 write!(f, "GazeProperties(TODO)") }
597}
598