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 AnnotationsMap, CocoSegmentation, ExportAsCoco, InstanceExportData, LabelInfo,
11 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
23pub 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 ExportAsCoco<GeoFig> for BboxToolData {
146 fn cocofile_conn(&self) -> ExportPath {
147 self.coco_file.clone()
148 }
149 fn separate_data(self) -> (core::Options, LabelInfo, AnnotationsMap<GeoFig>, ExportPath) {
150 (
151 self.options.core,
152 self.label_info,
153 self.annotations_map,
154 self.coco_file,
155 )
156 }
157 fn label_info(&self) -> &LabelInfo {
158 &self.label_info
159 }
160 #[cfg(test)]
161 fn anno_iter(&self) -> impl Iterator<Item = (&String, &(InstanceAnnotations<GeoFig>, ShapeI))> {
162 self.anno_iter()
163 }
164 fn core_options_mut(&mut self) -> &mut core::Options {
165 &mut self.options.core
166 }
167 fn new(
168 options: core::Options,
169 label_info: LabelInfo,
170 anno_map: AnnotationsMap<GeoFig>,
171 export_path: ExportPath,
172 ) -> Self {
173 Self {
174 label_info,
175 annotations_map: anno_map,
176 clipboard: None,
177 options: Options {
178 core: options,
179 ..Default::default()
180 },
181 coco_file: export_path,
182 highlight_circles: vec![],
183 }
184 }
185 fn set_annotations_map(&mut self, map: AnnotationsMap<GeoFig>) -> RvResult<()> {
186 self.set_annotations_map(map)
187 }
188 fn set_labelinfo(&mut self, info: LabelInfo) {
189 self.label_info = info;
190 }
191}
192
193impl InstanceAnnotate for GeoFig {
194 fn rot90_with_image_ntimes(self, shape: ShapeI, n: u8) -> RvResult<Self> {
195 Ok(match self {
196 Self::BB(bb) => Self::BB(bb.rot90_with_image_ntimes(shape, n)),
197 Self::Poly(poly) => Self::Poly(poly.rot90_with_image_ntimes(shape, n)),
198 })
199 }
200 fn is_contained_in_image(&self, shape: ShapeI) -> bool {
201 match self {
202 Self::BB(bb) => bb.is_contained_in_image(shape),
203 Self::Poly(poly) => poly.is_contained_in_image(shape),
204 }
205 }
206 fn enclosing_bb(&self) -> BbF {
207 match self {
208 Self::BB(bb) => *bb,
209 Self::Poly(poly) => poly.enclosing_bb(),
210 }
211 }
212 fn contains<P>(&self, point: P) -> bool
213 where
214 P: Into<PtF>,
215 {
216 match self {
217 Self::BB(bb) => bb.contains(point.into()),
218 Self::Poly(poly) => poly.contains(point),
219 }
220 }
221 fn dist_to_boundary(&self, point: PtF) -> TPtF {
222 match self {
223 Self::BB(bb) => bb.distance_to_boundary(point),
224 Self::Poly(poly) => poly.distance_to_boundary(point),
225 }
226 }
227 fn to_cocoseg(
228 &self,
229 shape_im: ShapeI,
230 is_export_absolute: bool,
231 ) -> RvResult<Option<core::CocoSegmentation>> {
232 Ok(Some(CocoSegmentation::Polygon(vec![
233 if is_export_absolute {
234 self.points()
235 } else {
236 self.points_normalized(TPtF::from(shape_im.w), TPtF::from(shape_im.h))
237 }
238 .iter()
239 .flat_map(|p| iter::once(p.x).chain(iter::once(p.y)))
240 .collect::<Vec<_>>(),
241 ])))
242 }
243}