rvlib/tools_data/
bbox_data.rs

1use std::iter;
2
3use serde::{Deserialize, Serialize};
4
5#[cfg(test)]
6use super::annotations::InstanceAnnotations;
7use super::{
8    annotations::{BboxAnnotations, ClipboardData},
9    core::{
10        AccessInstanceData, AnnotationsMap, CocoSegmentation, ExportAsCoco, InstanceExportData,
11        LabelInfo, OUTLINE_THICKNESS_CONVERSION,
12    },
13    InstanceAnnotate,
14};
15use crate::{
16    cfg::ExportPath,
17    file_util, implement_annotate, implement_annotations_getters,
18    tools_data::{annotations::SplitMode, core},
19    GeoFig,
20};
21use rvimage_domain::{rverr, BbF, Circle, PtF, RvResult, ShapeI, TPtF};
22
23/// filename -> (annotations per file, file dimensions)
24pub type BboxAnnoMap = AnnotationsMap<GeoFig>;
25
26#[derive(Clone, Copy, Deserialize, Serialize, Debug, PartialEq, Eq)]
27pub struct Options {
28    #[serde(skip)]
29    pub core: core::Options,
30    pub split_mode: SplitMode,
31    pub fill_alpha: u8,
32    pub outline_alpha: u8,
33    pub outline_thickness: u16,
34    pub drawing_distance: u8,
35}
36impl Default for Options {
37    fn default() -> Self {
38        Self {
39            core: core::Options::default(),
40            split_mode: SplitMode::default(),
41            fill_alpha: 30,
42            outline_alpha: 255,
43            outline_thickness: OUTLINE_THICKNESS_CONVERSION as u16,
44            drawing_distance: 10,
45        }
46    }
47}
48#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
49pub struct BboxToolData {
50    pub label_info: LabelInfo,
51    pub annotations_map: BboxAnnoMap,
52    #[serde(skip)]
53    pub clipboard: Option<ClipboardData<GeoFig>>,
54    pub options: Options,
55    pub coco_file: ExportPath,
56    #[serde(skip)]
57    pub highlight_circles: Vec<Circle>,
58}
59
60impl BboxToolData {
61    implement_annotations_getters!(BboxAnnotations);
62
63    pub fn separate_data(self) -> (LabelInfo, BboxAnnoMap, ExportPath) {
64        (self.label_info, self.annotations_map, self.coco_file)
65    }
66
67    pub fn from_coco_export_data(input_data: InstanceExportData<GeoFig>) -> RvResult<Self> {
68        let label_info = input_data.label_info()?;
69        let mut out_data = Self {
70            label_info,
71            annotations_map: AnnotationsMap::new(),
72            clipboard: None,
73            options: Options {
74                core: core::Options {
75                    visible: true,
76                    ..Default::default()
77                },
78                ..Default::default()
79            },
80            coco_file: input_data.coco_file,
81            highlight_circles: vec![],
82        };
83        out_data.set_annotations_map(
84            input_data
85                .annotations
86                .into_iter()
87                .map(|(s, (geos, cat_ids, dims))| {
88                    (s, (BboxAnnotations::from_elts_cats(geos, cat_ids), dims))
89                })
90                .collect(),
91        )?;
92        Ok(out_data)
93    }
94
95    pub fn retain_fileannos_in_folder(&mut self, folder: &str) {
96        self.annotations_map
97            .retain(|f, _| file_util::url_encode(f).starts_with(folder));
98    }
99
100    pub fn new() -> Self {
101        let label_info = LabelInfo::default();
102
103        BboxToolData {
104            label_info,
105            annotations_map: AnnotationsMap::new(),
106            clipboard: None,
107            options: Options {
108                core: core::Options {
109                    visible: true,
110                    ..Default::default()
111                },
112                ..Default::default()
113            },
114            coco_file: ExportPath::default(),
115            highlight_circles: vec![],
116        }
117    }
118
119    pub fn set_annotations_map(&mut self, map: BboxAnnoMap) -> RvResult<()> {
120        for (_, (annos, _)) in map.iter() {
121            for cat_idx in annos.cat_idxs() {
122                let len = self.label_info.len();
123                if *cat_idx >= len {
124                    return Err(rverr!(
125                        "cat idx {} does not have a label, out of bounds, {}",
126                        cat_idx,
127                        len
128                    ));
129                }
130            }
131        }
132        self.annotations_map = map;
133        Ok(())
134    }
135}
136
137implement_annotate!(BboxToolData);
138
139impl Default for BboxToolData {
140    fn default() -> Self {
141        Self::new()
142    }
143}
144
145impl AccessInstanceData<GeoFig> for BboxToolData {
146    fn annotations_map(&self) -> &AnnotationsMap<GeoFig> {
147        &self.annotations_map
148    }
149    fn label_info(&self) -> &LabelInfo {
150        &self.label_info
151    }
152}
153
154impl ExportAsCoco<GeoFig> for BboxToolData {
155    fn cocofile_conn(&self) -> ExportPath {
156        self.coco_file.clone()
157    }
158    fn separate_data(self) -> (core::Options, LabelInfo, AnnotationsMap<GeoFig>, ExportPath) {
159        (
160            self.options.core,
161            self.label_info,
162            self.annotations_map,
163            self.coco_file,
164        )
165    }
166    #[cfg(test)]
167    fn anno_iter(&self) -> impl Iterator<Item = (&String, &(InstanceAnnotations<GeoFig>, ShapeI))> {
168        self.anno_iter()
169    }
170    fn core_options_mut(&mut self) -> &mut core::Options {
171        &mut self.options.core
172    }
173    fn new(
174        options: core::Options,
175        label_info: LabelInfo,
176        anno_map: AnnotationsMap<GeoFig>,
177        export_path: ExportPath,
178    ) -> Self {
179        Self {
180            label_info,
181            annotations_map: anno_map,
182            clipboard: None,
183            options: Options {
184                core: options,
185                ..Default::default()
186            },
187            coco_file: export_path,
188            highlight_circles: vec![],
189        }
190    }
191    fn set_annotations_map(&mut self, map: AnnotationsMap<GeoFig>) -> RvResult<()> {
192        self.set_annotations_map(map)
193    }
194    fn set_labelinfo(&mut self, info: LabelInfo) {
195        self.label_info = info;
196    }
197}
198
199impl InstanceAnnotate for GeoFig {
200    fn rot90_with_image_ntimes(self, shape: ShapeI, n: u8) -> RvResult<Self> {
201        Ok(match self {
202            Self::BB(bb) => Self::BB(bb.rot90_with_image_ntimes(shape, n)),
203            Self::Poly(poly) => Self::Poly(poly.rot90_with_image_ntimes(shape, n)),
204        })
205    }
206    fn is_contained_in_image(&self, shape: ShapeI) -> bool {
207        match self {
208            Self::BB(bb) => bb.is_contained_in_image(shape),
209            Self::Poly(poly) => poly.is_contained_in_image(shape),
210        }
211    }
212    fn enclosing_bb(&self) -> BbF {
213        match self {
214            Self::BB(bb) => *bb,
215            Self::Poly(poly) => poly.enclosing_bb(),
216        }
217    }
218    fn contains<P>(&self, point: P) -> bool
219    where
220        P: Into<PtF>,
221    {
222        match self {
223            Self::BB(bb) => bb.contains(point.into()),
224            Self::Poly(poly) => poly.contains(point),
225        }
226    }
227    fn dist_to_boundary(&self, point: PtF) -> TPtF {
228        match self {
229            Self::BB(bb) => bb.distance_to_boundary(point),
230            Self::Poly(poly) => poly.distance_to_boundary(point),
231        }
232    }
233    fn to_cocoseg(
234        &self,
235        shape_im: ShapeI,
236        is_export_absolute: bool,
237    ) -> RvResult<Option<core::CocoSegmentation>> {
238        Ok(Some(CocoSegmentation::Polygon(vec![
239            if is_export_absolute {
240                self.points()
241            } else {
242                self.points_normalized(TPtF::from(shape_im.w), TPtF::from(shape_im.h))
243            }
244            .iter()
245            .flat_map(|p| iter::once(p.x).chain(iter::once(p.y)))
246            .collect::<Vec<_>>(),
247        ])))
248    }
249}