ascom_alpaca/api/camera/image_array/
mod.rs

1#[cfg(feature = "client")]
2mod client;
3#[cfg(feature = "server")]
4mod server;
5
6#[cfg(feature = "server")]
7pub(crate) use server::ImageBytesResponse;
8
9use bytemuck::{AnyBitPattern, Pod, Zeroable};
10use ndarray::{Array2, Array3, ArrayView2, ArrayView3, Axis};
11use num_enum::{IntoPrimitive, TryFromPrimitive};
12use serde_repr::{Deserialize_repr, Serialize_repr};
13use std::num::NonZeroU32;
14
15// Missing alias in ndarray.
16type ArcArray3<T> = ndarray::ArcArray<T, ndarray::Ix3>;
17
18/// Rank of an image array.
19#[derive(
20    Debug,
21    PartialEq,
22    Eq,
23    Clone,
24    Copy,
25    Serialize_repr,
26    Deserialize_repr,
27    TryFromPrimitive,
28    IntoPrimitive,
29)]
30#[repr(i32)]
31pub enum ImageArrayRank {
32    /// 2D
33    Rank2 = 2_i32,
34    /// 3D
35    Rank3 = 3_i32,
36}
37
38#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
39#[repr(i32)]
40pub(crate) enum TransmissionElementType {
41    I16 = 1,
42    I32 = 2,
43    U8 = 6,
44    U16 = 8,
45}
46
47// Limited to the only supported element type; useful for serde purposes.
48#[derive(
49    Debug,
50    PartialEq,
51    Eq,
52    Clone,
53    Copy,
54    Serialize_repr,
55    Deserialize_repr,
56    IntoPrimitive,
57    TryFromPrimitive,
58)]
59#[repr(i32)]
60pub(crate) enum ImageElementType {
61    /// See [`TransmissionElementType::I32`].
62    I32 = 2,
63}
64
65trait AsTransmissionElementType: 'static + Into<i32> + AnyBitPattern {
66    const TYPE: TransmissionElementType;
67}
68
69impl AsTransmissionElementType for i16 {
70    const TYPE: TransmissionElementType = TransmissionElementType::I16;
71}
72
73impl AsTransmissionElementType for i32 {
74    const TYPE: TransmissionElementType = TransmissionElementType::I32;
75}
76
77impl AsTransmissionElementType for u16 {
78    const TYPE: TransmissionElementType = TransmissionElementType::U16;
79}
80
81impl AsTransmissionElementType for u8 {
82    const TYPE: TransmissionElementType = TransmissionElementType::U8;
83}
84
85/// Image array.
86///
87/// Image is represented as a 3D array regardless of its actual rank.
88/// If the image is a 2D image, the third dimension will have length 1.
89///
90/// You can retrieve rank as an enum via the [`ImageArray::rank`] method.
91///
92/// This type is cheaply clonable.
93#[derive(Debug, PartialEq, Eq, Clone, derive_more::Deref)]
94pub struct ImageArray {
95    #[deref]
96    data: ArcArray3<i32>,
97    transmission_element_type: TransmissionElementType,
98}
99
100const COLOUR_AXIS: Axis = Axis(2);
101
102impl<T: AsTransmissionElementType> From<ArrayView3<'_, T>> for ImageArray {
103    fn from(array: ArrayView3<'_, T>) -> Self {
104        let data = array.mapv(Into::into);
105        let transmission_element_type = T::TYPE;
106        Self {
107            data: data.into_shared(),
108            transmission_element_type,
109        }
110    }
111}
112
113impl<T: AsTransmissionElementType> From<Array3<T>> for ImageArray {
114    fn from(array: Array3<T>) -> Self {
115        let data = array.mapv_into_any(Into::into);
116        let transmission_element_type = T::TYPE;
117        Self {
118            data: data.into_shared(),
119            transmission_element_type,
120        }
121    }
122}
123
124impl<T: AsTransmissionElementType> From<ArrayView2<'_, T>> for ImageArray {
125    fn from(array: ArrayView2<'_, T>) -> Self {
126        array.insert_axis(COLOUR_AXIS).into()
127    }
128}
129
130impl<T: AsTransmissionElementType> From<Array2<T>> for ImageArray {
131    fn from(array: Array2<T>) -> Self {
132        array.insert_axis(COLOUR_AXIS).into()
133    }
134}
135
136impl ImageArray {
137    /// Retrieve actual rank of the image.
138    pub fn rank(&self) -> ImageArrayRank {
139        match self.data.len_of(COLOUR_AXIS) {
140            1 => ImageArrayRank::Rank2,
141            _ => ImageArrayRank::Rank3,
142        }
143    }
144}
145
146#[cfg(not(target_endian = "little"))]
147compile_error!(
148"Image handling is currently only supported on little-endian platforms for simplicity & performance.
149If you have a real-world use case for big-endian support, please open an issue on GitHub."
150);
151
152#[cfg(any(feature = "client", feature = "server"))]
153#[repr(C)]
154#[derive(Clone, Copy, Zeroable, Pod)]
155struct ImageBytesMetadata {
156    metadata_version: i32,
157    error_number: i32,
158    client_transaction_id: Option<NonZeroU32>,
159    server_transaction_id: Option<NonZeroU32>,
160    data_start: i32,
161    image_element_type: i32,
162    transmission_element_type: i32,
163    rank: i32,
164    dimension_1: i32,
165    dimension_2: i32,
166    dimension_3: i32,
167}
168
169const IMAGE_BYTES_TYPE: &str = "application/imagebytes";