11use std:: ops:: RangeTo ;
22use std:: path:: { Path , PathBuf } ;
33use std:: time:: Instant ;
4- use anyhow:: Result ;
54use image:: { GenericImage , ImageBuffer , Rgba , RgbaImage } ;
65use image:: imageops:: FilterType ;
76use imageproc:: drawing:: draw_text_mut;
87use log:: { debug, trace} ;
9- use rand:: { Rng , thread_rng } ;
8+ use rand:: { thread_rng , Rng } ;
109use simple_logger:: SimpleLogger ;
1110use strum:: Display ;
1211use thiserror:: Error ;
12+ use error:: GenerationError ;
13+ use util:: STANDARD_PPM ;
1314use crate :: backgrounds:: BackgroundLoader ;
1415use crate :: generator:: coco:: { CocoCategoryInfo , CocoGenerator } ;
15- use crate :: objects:: { ObjectClass , ObjectManager } ;
16+ use crate :: generator:: config:: TargetGeneratorConfig ;
17+ use crate :: objects:: { ObjectClass , ObjectManager , PlacedObject } ;
1618
1719pub mod coco;
20+ pub mod error;
21+ pub ( crate ) mod util;
22+ pub mod config;
1823
1924pub struct TargetGenerator {
2025 output : PathBuf ,
2126 backgrounds_path : PathBuf ,
2227 pub object_manager : ObjectManager ,
2328 background_loader : BackgroundLoader ,
24- coco_generator : CocoGenerator
29+ coco_generator : CocoGenerator ,
30+ config : TargetGeneratorConfig ,
2531}
2632
2733impl TargetGenerator {
28- pub fn new < Q : AsRef < Path > > ( output : Q , background_path : Q , objects_path : Q , annotations_path : Q ) -> Result < Self > {
34+ pub fn new < Q : AsRef < Path > > ( output : Q , background_path : Q , objects_path : Q , annotations_path : Q ) -> Result < Self , GenerationError > {
2935
3036 let mut object_manager = ObjectManager :: new ( objects_path) ;
3137 object_manager. load_objects ( ) ?;
@@ -35,51 +41,67 @@ impl TargetGenerator {
3541 backgrounds_path : background_path. as_ref ( ) . to_path_buf ( ) ,
3642 object_manager,
3743 background_loader : BackgroundLoader :: new ( background_path) ?,
38- coco_generator : CocoGenerator :: new ( annotations_path, ObjectClass :: categories ( ) )
44+ coco_generator : CocoGenerator :: new ( annotations_path, ObjectClass :: categories ( ) ) ,
45+ config : TargetGeneratorConfig :: default ( ) ,
3946 } )
4047 }
4148
42- pub fn generate_target ( & mut self , pixels_per_meter : f32 ) -> Result < RgbaImage > {
49+ pub fn generate_target ( & mut self , pixels_per_meter : f32 , number_of_objects : u16 ) -> Result < RgbaImage , GenerationError > {
4350 trace ! ( "Beginning to generate a target..." ) ;
51+
52+ if number_of_objects == 0 {
53+ return Err ( GenerationError :: NoObjects ) ;
54+ }
4455
4556 let background = self . background_loader . random ( ) . unwrap ( ) ;
4657 let mut image = background. image . clone ( ) ;
4758 let ( w, h) = ( image. width ( ) , image. height ( ) ) ;
48- let set = self . object_manager . generate_set ( 1 ) ?;
59+ let set = self . object_manager . generate_set ( number_of_objects as u32 ) ?;
60+ let mut placed_objects = vec ! [ ] ;
4961
5062 // add background image to coco here
51- self . coco_generator . add_image ( w, h, background. filename . clone ( ) , background. date_captured . clone ( ) ) ;
63+ let background_id = self . coco_generator . add_image ( w, h, background. filename . clone ( ) , background. date_captured . clone ( ) ) ;
5264
5365 for obj in set {
5466 let clone = & obj. dynamic_image . clone ( ) ;
5567 let ( obj_w, obj_h) = ( obj. dynamic_image . width ( ) , obj. dynamic_image . height ( ) ) ;
5668 let ( x, y) = ( thread_rng ( ) . gen_range ( 0 ..w - obj_w) , thread_rng ( ) . gen_range ( 0 ..h - obj_h) ) ;
5769 trace ! ( "Placing object at {}, {}" , x, y) ;
5870
59- let ( obj_w, obj_h) = new_sizes ( obj_w, obj_h, pixels_per_meter, obj. object_width_meters ) ?;
71+ let ( obj_w, obj_h) = util :: new_sizes ( obj_w, obj_h, pixels_per_meter, obj. object_width_meters ) ?;
6072 debug ! ( "Resizing object to {}x{}" , obj_w, obj_h) ;
6173
6274 // overlay respects transparent pixels unlike copy_from
6375 image:: imageops:: overlay ( & mut image, & clone. resize ( obj_w, obj_h, FilterType :: Gaussian ) , x as i64 , y as i64 ) ;
6476
65- // TODO: remove, both are top left
66- //imageproc::drawing::draw_filled_circle_mut(&mut image, (x as i32, y as i32), 4, Rgba([255, 0, 0, 255]));
67- //imageproc::drawing::draw_filled_circle_mut(&mut image, (0i32, 0i32), 8, Rgba([255, 0, 255, 255]));
68- imageproc:: drawing:: draw_hollow_rect_mut ( & mut image, imageproc:: rect:: Rect :: at ( x as i32 , y as i32 ) . of_size ( obj_w, obj_h) , Rgba ( [ 0 , 255 , 0 , 255 ] ) ) ;
77+ if self . config . visualize_bboxes {
78+ imageproc:: drawing:: draw_hollow_rect_mut ( & mut image, imageproc:: rect:: Rect :: at ( x as i32 , y as i32 ) . of_size ( obj_w, obj_h) , Rgba ( [ 0 , 255 , 0 , 255 ] ) ) ;
79+ }
6980
70- // add annotation to coco here
71- self . coco_generator . add_annotation ( 0 , obj. object_class as u32 , 0 , vec ! [ ] , ( obj_w * obj_h) as f64 , coco:: BoundingBox {
81+ if let Some ( color) = self . config . maskover_color {
82+ imageproc:: drawing:: draw_filled_rect_mut ( & mut image, imageproc:: rect:: Rect :: at ( x as i32 , y as i32 ) . of_size ( obj_w, obj_h) , color) ;
83+ }
84+
85+ let bbox = coco:: BoundingBox {
7286 x,
7387 y,
7488 width : obj_w,
7589 height : obj_h,
90+ } ;
91+
92+ // add annotation to coco here
93+ let object_id = self . coco_generator . add_annotation ( background_id, obj. object_class as u32 , 0 , vec ! [ ] , ( obj_w * obj_h) as f64 , bbox) ;
94+
95+ placed_objects. push ( PlacedObject {
96+ id : object_id,
97+ bounding_box : bbox,
7698 } ) ;
7799 }
78100
79101 Ok ( image)
80102 }
81103
82- pub fn generate_targets < A : AsRef < Path > > ( & self , amount : u32 , range_to : RangeTo < u32 > , path : A ) -> Result < ( ) > {
104+ pub fn generate_targets < A : AsRef < Path > > ( & self , amount : u32 , range_to : RangeTo < u32 > , path : A ) -> Result < ( ) , GenerationError > {
83105 let start = Instant :: now ( ) ; // start timer
84106
85107
@@ -89,45 +111,13 @@ impl TargetGenerator {
89111 Ok ( ( ) )
90112 }
91113
92- pub fn close ( & self ) {
93- self . coco_generator . save ( ) ;
114+ pub fn generate_new_location_no_collision ( & self ) -> ( u32 , u32 ) {
115+ todo ! ( ) // TODO: use collision detection to find a new location
94116 }
95- }
96-
97- const STANDARD_PPM : f32 = 35.0 ;
98-
99- /// Use the real size of an object and the Pixels Per Meter value to calculate the size in
100- /// pixels that it should be in order to be at scale
101- fn resize_ratio ( object_real_size : f32 , pixels_per_meter : f32 ) -> f32 {
102- debug ! ( "Real size: {}, Pixels per meter: {}" , object_real_size, pixels_per_meter) ;
103- object_real_size * pixels_per_meter
104- }
105-
106- /// Calculate the new sizes of an object in pixels based on the requested Pixel Per Meter value
107- /// 1. Calculate the aspect ratio
108- /// 2. Calculate the width of the object in pixels that we expect based on the real width and the Pixels Per Meter value
109- /// 3. Calculate the height from this new width using the previously calculated aspect ratio
110- fn new_sizes ( object_width : u32 , object_height : u32 , pixels_per_meter : f32 , real_width : f32 ) -> Result < ( u32 , u32 ) , GenerationError > {
111- let ( w, h) = ( object_width as f32 , object_height as f32 ) ;
112- let aspect_ratio = w / h;
113- let new_width = resize_ratio ( real_width, pixels_per_meter) as u32 ;
114- let new_height = ( new_width as f32 / aspect_ratio) as u32 ;
115117
116- if new_height == 0 || new_width == 0 {
117- return Err ( GenerationError :: SizeError ) ;
118+ pub fn close ( & self ) {
119+ self . coco_generator . save ( ) ;
118120 }
119-
120- Ok ( ( new_width, new_height) )
121- }
122-
123- #[ derive( Debug , Error ) ]
124- pub enum GenerationError {
125- #[ error( "Serde decoding or encoding error" ) ]
126- SerdeError ( #[ from] serde_json:: Error ) ,
127- #[ error( "IO error occurred while generating target" ) ]
128- IOError ( #[ from] std:: io:: Error ) ,
129- #[ error( "Calculated new sizes provided an invalid size" ) ]
130- SizeError
131121}
132122
133123#[ test]
@@ -136,7 +126,7 @@ pub fn test_generate_target() {
136126 SimpleLogger :: new ( ) . init ( ) . unwrap ( ) ;
137127
138128 let mut tg = TargetGenerator :: new ( "output" , "backgrounds" , "objects" , "output/annotations.json" ) . unwrap ( ) ;
139- let b = tg. generate_target ( STANDARD_PPM ) . unwrap ( ) ;
129+ let b = tg. generate_target ( STANDARD_PPM , 1 ) . unwrap ( ) ;
140130
141131 b. save ( "output_1.png" . to_string ( ) ) . unwrap ( ) ;
142132 debug ! ( "Saved generated target to output_1.png" ) ;
0 commit comments