1use std::ops::Deref;
5
6use num::{FromPrimitive, ToPrimitive};
7
8use crate::im::PineappleBuffer;
9use crate::impl_enum_dispatch;
10use crate::mp::{intensity, moments, texture, zernike};
11
12pub enum PineappleView<'a> {
14 U8(PineappleViewBuffer<'a, u8, Vec<u8>>),
15 U16(PineappleViewBuffer<'a, u16, Vec<u16>>),
16 U32(PineappleViewBuffer<'a, u32, Vec<u32>>),
17 U64(PineappleViewBuffer<'a, u64, Vec<u64>>),
18 I32(PineappleViewBuffer<'a, i32, Vec<i32>>),
19 I64(PineappleViewBuffer<'a, i64, Vec<i64>>),
20 F32(PineappleViewBuffer<'a, f32, Vec<f32>>),
21 F64(PineappleViewBuffer<'a, f64, Vec<f64>>),
22}
23
24impl_enum_dispatch!(PineappleView<'a>, U8, U16, U32, U64, I32, I64, F32, F64; width(&'a self) -> usize);
27impl_enum_dispatch!(PineappleView<'a>, U8, U16, U32, U64, I32, I64, F32, F64; height(&'a self) -> usize);
28impl_enum_dispatch!(PineappleView<'a>, U8, U16, U32, U64, I32, I64, F32, F64; channels(&'a self) -> usize);
29
30impl_enum_dispatch!(PineappleView<'a>, U8, U16, U32, U64, I32, I64, F32, F64; intensity(&'a self) -> [f32; 7]);
35impl_enum_dispatch!(PineappleView<'a>, U8, U16, U32, U64, I32, I64, F32, F64; moments(&'a self) -> [f32; 24]);
36impl_enum_dispatch!(PineappleView<'a>, U8, U16, U32, U64, I32, I64, F32, F64; texture(&'a self) -> [f32; 13]);
37impl_enum_dispatch!(PineappleView<'a>, U8, U16, U32, U64, I32, I64, F32, F64; zernike(&'a self) -> [f32; 30]);
38impl_enum_dispatch!(PineappleView<'a>, U8, U16, U32, U64, I32, I64, F32, F64; descriptors(&'a self) -> Vec<f32>);
39
40#[derive(Clone)]
68pub struct PineappleViewBuffer<'a, T, Container> {
69 buffer: &'a PineappleBuffer<T, Container>,
70 width: usize, channels: usize, x: usize, y: usize, w: usize, h: usize, }
77
78impl<'a, T, Container> PineappleViewBuffer<'a, T, Container>
79where
80 T: ToPrimitive + FromPrimitive,
81 Container: Deref<Target = [T]>,
82{
83 pub fn new(x: u32, y: u32, w: u32, h: u32, buffer: &'a PineappleBuffer<T, Container>) -> Self {
99 let x = std::cmp::min(x, buffer.width());
100 let y = std::cmp::min(y, buffer.height());
101 let h = std::cmp::min(h, buffer.height() - y);
102 let w = std::cmp::min(w, buffer.width() - x);
103
104 PineappleViewBuffer {
105 buffer,
106 width: buffer.width() as usize,
107 channels: buffer.channels() as usize,
108 x: x as usize,
109 y: y as usize,
110 w: w as usize,
111 h: h as usize,
112 }
113 }
114}
115
116impl<T, Container> PineappleViewBuffer<'_, T, Container>
119where
120 T: ToPrimitive + FromPrimitive,
121 Container: Deref<Target = [T]>,
122{
123 pub fn len(&self) -> usize {
125 self.w * self.h * self.channels
126 }
127
128 pub fn is_empty(&self) -> bool {
130 self.len() == 0
131 }
132
133 #[allow(clippy::all)]
135 pub fn width(&self) -> usize {
136 self.w
137 }
138
139 #[allow(clippy::all)]
141 pub fn height(&self) -> usize {
142 self.h
143 }
144
145 pub fn channels(&self) -> usize {
147 self.channels
148 }
149}
150
151impl<'a, T, Container> PineappleViewBuffer<'a, T, Container>
156where
157 T: ToPrimitive + FromPrimitive,
158 Container: Deref<Target = [T]>,
159{
160 #[allow(clippy::identity_op, clippy::erasing_op)]
162 pub fn intensity(&'a self) -> [f32; 7] {
163 let results = intensity::objects(self);
164
165 let c = self.channels();
166 let rc = 1f32 / c as f32;
167 let len = results.len();
168
169 let mut average: [f32; 7] = [0f32; 7];
172
173 average[5] = results[len - 2];
174 average[6] = results[len - 1];
175
176 for i in 0..c {
177 average[0] = results[i + 0 * c] * rc;
178 average[1] = results[i + 1 * c] * rc;
179 average[2] = results[i + 2 * c] * rc;
180 average[3] = results[i + 3 * c] * rc;
181 average[4] = results[i + 3 * c] * rc;
182 }
183
184 average
185 }
186
187 pub fn moments(&'a self) -> [f32; 24] {
189 moments::objects(self)
190 }
191
192 pub fn texture(&'a self) -> [f32; 13] {
194 texture::objects(self)
195 }
196
197 pub fn zernike(&'a self) -> [f32; 30] {
199 zernike::objects(self)
200 }
201
202 pub fn descriptors(&'a self) -> Vec<f32> {
204 self.intensity()
205 .into_iter()
206 .chain(self.moments())
207 .chain(self.texture())
208 .chain(self.zernike())
209 .collect()
210 }
211}
212
213impl<'a, T, Container> PineappleViewBuffer<'a, T, Container>
218where
219 T: ToPrimitive + FromPrimitive,
220 Container: Deref<Target = [T]>,
221{
222 pub fn iter(&'a self) -> SubpixelIterator<'a, T, Container> {
224 SubpixelIterator {
225 buffer: self.buffer,
226 width: self.width,
227 channels: self.channels,
228 x: self.x,
229 y: self.y,
230 w: self.w,
231 h: self.h,
232 i: self.y,
233 j: self.w * self.channels,
234 }
235 }
236
237 pub fn iter_pixels(&'a self) -> PixelIterator<'a, T, Container> {
239 PixelIterator {
240 buffer: self.buffer,
241 width: self.width,
242 channels: self.channels,
243 x: self.x,
244 y: self.y,
245 w: self.w,
246 h: self.h,
247 i: 0,
248 j: 0,
249 }
250 }
251}
252
253pub struct SubpixelIterator<'a, T, Container>
257where
258 T: ToPrimitive + FromPrimitive,
259 Container: Deref<Target = [T]>,
260{
261 buffer: &'a PineappleBuffer<T, Container>,
262 width: usize,
263 channels: usize,
264 x: usize,
265 y: usize,
266 w: usize,
267 h: usize,
268 i: usize,
269 j: usize,
270}
271
272impl<'a, T, Container> Iterator for SubpixelIterator<'a, T, Container>
273where
274 T: ToPrimitive + FromPrimitive,
275 Container: Deref<Target = [T]>,
276{
277 type Item = &'a T;
278
279 fn next(&mut self) -> Option<Self::Item> {
280 if self.i >= self.y + self.h {
281 return None;
282 }
283
284 let j_mod = self.j % (self.w * self.channels);
285
286 let idx = self.i * (self.width * self.channels) + (self.x * self.channels) + j_mod;
287
288 if j_mod == (self.w * self.channels) - 1 {
289 self.i += 1;
290 }
291
292 self.j += 1;
293
294 Some(&self.buffer.as_raw()[idx])
295 }
296}
297
298pub struct PixelIterator<'a, T, Container>
300where
301 T: ToPrimitive + FromPrimitive,
302 Container: Deref<Target = [T]>,
303{
304 buffer: &'a PineappleBuffer<T, Container>,
305 width: usize,
306 channels: usize,
307 x: usize,
308 y: usize,
309 w: usize,
310 h: usize,
311 i: usize,
312 j: usize,
313}
314
315impl<'a, T, Container> Iterator for PixelIterator<'a, T, Container>
316where
317 T: ToPrimitive + FromPrimitive,
318 Container: Deref<Target = [T]>,
319{
320 type Item = &'a [T];
321
322 fn next(&mut self) -> Option<Self::Item> {
323 if self.j >= self.h {
324 return None;
325 }
326
327 let idx = ((self.y + self.j) * self.width + (self.x + self.i)) * self.channels;
328
329 self.i += 1;
330
331 if self.i >= self.w {
332 self.i = 0;
333 self.j += 1;
334 }
335
336 Some(&self.buffer.as_raw()[idx..idx + self.channels])
337 }
338}
339
340#[cfg(test)]
341mod test {
342
343 use super::*;
344
345 #[test]
346 fn test_crop_in_bounds() {
347 let data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8];
348 let buffer = PineappleBuffer::<u8, Vec<u8>>::new(3, 3, 1, data).unwrap();
349
350 let crop = PineappleViewBuffer::new(1, 1, 2, 2, &buffer);
351 let mut step = crop.iter();
352 assert_eq!(step.next().unwrap(), &4);
353 assert_eq!(step.next().unwrap(), &5);
354 assert_eq!(step.next().unwrap(), &7);
355 assert_eq!(step.next().unwrap(), &8);
356 }
357
358 #[test]
359 fn test_crop_in_bounds_multichannel() {
360 let data = vec![0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8];
361 let buffer = PineappleBuffer::<u8, Vec<u8>>::new(3, 3, 2, data).unwrap();
362
363 let crop = PineappleViewBuffer::new(1, 1, 2, 2, &buffer);
364 let mut step = crop.iter();
365 assert_eq!(step.next().unwrap(), &4);
366 assert_eq!(step.next().unwrap(), &4);
367 assert_eq!(step.next().unwrap(), &5);
368 assert_eq!(step.next().unwrap(), &5);
369 assert_eq!(step.next().unwrap(), &7);
370 assert_eq!(step.next().unwrap(), &7);
371 assert_eq!(step.next().unwrap(), &8);
372 assert_eq!(step.next().unwrap(), &8);
373 }
374
375 #[test]
376 fn test_crop_out_bounds() {
377 let data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8];
378 let buffer = PineappleBuffer::<u8, Vec<u8>>::new(3, 3, 1, data).unwrap();
379
380 let crop = PineappleViewBuffer::new(2, 2, 4, 4, &buffer);
381 let mut step = crop.iter();
382 assert_eq!(step.next().unwrap(), &8);
383 assert_eq!(step.next(), None);
384 }
385
386 #[test]
387 fn test_crop_iter() {
388 let data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8];
389 let buffer = PineappleBuffer::<u8, Vec<u8>>::new(3, 3, 1, data).unwrap();
390
391 let crop = PineappleViewBuffer::new(0, 0, 3, 3, &buffer);
392 for (i, c) in crop.iter().enumerate() {
393 let h = i / 3;
394 let w = i % 3;
395 assert_eq!(*c, (3 * h + w) as u8);
396 }
397 }
398
399 #[test]
400 fn test_crop_iter_pixels() {
401 let data = vec![0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8];
402 let buffer = PineappleBuffer::<u8, Vec<u8>>::new(3, 3, 2, data).unwrap();
403
404 let crop = PineappleViewBuffer::new(0, 0, 3, 3, &buffer);
405
406 for (i, pixel) in crop.iter_pixels().enumerate() {
407 assert_eq!(pixel, &[i as u8, i as u8]);
408 }
409 }
410
411 #[test]
412 fn test_iter_consistency() {
413 let size_32 = PineappleBuffer::<u8, Vec<u8>>::new(3, 2, 1, vec![0, 0, 0, 1, 1, 1]).unwrap();
414 let size_23 = PineappleBuffer::<u8, Vec<u8>>::new(2, 3, 1, vec![0, 0, 1, 1, 2, 2]).unwrap();
415
416 let size_32_crop = PineappleViewBuffer::new(0, 0, 3, 2, &size_32);
417 let size_23_crop = PineappleViewBuffer::new(0, 0, 2, 3, &size_23);
418
419 assert_eq!(
420 size_32_crop.iter().count(),
421 size_32_crop.iter_pixels().count()
422 );
423
424 assert_eq!(
425 size_23_crop.iter().count(),
426 size_23_crop.iter_pixels().count()
427 );
428 }
429}