vectorizer/actions/
common.rs1mod private
22{
23 use crate::{ commands::InputOutput, svg::SvgFile, * };
24 use std::io::Write;
25 use actions::{ Error, Result };
26 use std::collections::HashMap;
27 use fastrand::Rng;
28 use palette::{ color_difference::EuclideanDistance, IntoColor, Lab, Srgb };
29 use visioncortex::Color;
30
31 const NUM_UNUSED_COLOR_ITERATIONS: usize = 6;
32 const KEYING_THRESHOLD: f32 = 0.2;
33
34 pub fn find_unused_color_in_image( img : &visioncortex::ColorImage ) -> Result < visioncortex::Color >
37 {
38 let special_colors = IntoIterator::into_iter
39 ([
40 visioncortex::Color::new( 255, 0, 0 ),
41 visioncortex::Color::new( 0, 255, 0 ),
42 visioncortex::Color::new( 0, 0, 255 ),
43 visioncortex::Color::new( 255, 255, 0 ),
44 visioncortex::Color::new( 0, 255, 255 ),
45 visioncortex::Color::new( 255, 0, 255 ),
46 ]);
47 let rng = Rng::new();
48 let random_colors = ( 0..NUM_UNUSED_COLOR_ITERATIONS )
49 .map( | _ | visioncortex::Color::new( rng.u8( ..) , rng.u8( .. ), rng.u8( .. ) ) );
50
51 for color in special_colors.chain( random_colors )
52 {
53 if !color_exists_in_image( img, color )
54 {
55 return Ok( color );
56 }
57 }
58 Err
59 (
60 Error::KeyColorError
61 )
62 }
63
64 pub fn color_exists_in_image( img : &visioncortex::ColorImage, color : visioncortex::Color ) -> bool
66 {
67 for y in 0..img.height
68 {
69 for x in 0..img.width
70 {
71 let pixel_color = img.get_pixel( x, y );
72 if pixel_color.r == color.r &&
73 pixel_color.g == color.g &&
74 pixel_color.b == color.b
75 {
76 return true;
77 }
78 }
79 }
80 false
81 }
82
83 pub fn should_key_image( img : &visioncortex::ColorImage ) -> bool
95 {
96 let ( width, height ) = ( img.width, img.height );
97 if width == 0 || height == 0
98 {
99 return false;
100 }
101
102 let threshold = ( ( width * 2 ) as f32 * KEYING_THRESHOLD ) as usize;
105 let mut num_transparent_pixels = 0;
106 let y_positions =
107 [
108 0,
109 height / 4,
110 height / 2,
111 3 * height / 4,
112 height - 1,
113 ];
114
115 for y in y_positions
116 {
117 for x in 0..width
118 {
119 if img.get_pixel( x, y ).a == 0
120 {
121 num_transparent_pixels += 1;
122 }
123 if num_transparent_pixels >= threshold
124 {
125 return true;
126 }
127 }
128 }
129
130 false
131 }
132
133
134 pub fn background_color( img : &visioncortex::ColorImage, mask : u8 ) -> Option< [ u8; 3 ] >
136 {
137 let mut unique_colors = HashMap::new();
138
139 for y in [ 0, img.height - 1 ]
140 {
141 for x in 0..img.width
142 {
143 let c = img.get_pixel( x, y );
144 if c.a > 0
145 {
146 unique_colors.entry( [ c.r & mask, c.g & mask, c.b & mask ] )
147 .and_modify( | v | *v += 1 )
148 .or_insert( 1 );
149 }
150 }
151 }
152
153 for x in [ 0, img.width - 1 ]
154 {
155 for y in 0..img.height
156 {
157 let c = img.get_pixel( x, y );
158 if c.a > 0
159 {
160 unique_colors.entry( [ c.r & mask, c.g & mask, c.b & mask ] )
161 .and_modify( | v | *v += 1 )
162 .or_insert( 1 );
163 }
164 }
165 }
166
167 let mut colors : Vec< ( [ u8; 3 ], u32 ) > = unique_colors.into_iter().collect();
168 colors.sort_unstable_by_key( | ( _, count ) | *count );
169
170
171 colors.last().map( | ( col, _ ) | *col )
172 }
173
174 pub fn euclid_difference( c1 : Color, c2 : Color ) -> f32
176 {
177 let c1 = Srgb::from( [ c1.r, c1.g, c1.b ] ).into_linear::< f32 >();
178 let c2 = Srgb::from( [ c2.r, c2.g, c2.b ] ).into_linear::< f32 >();
179 let lab_c1 : Lab = c1.into_color();
180 let lab_c2 : Lab = c2.into_color();
181 lab_c1.distance_squared( lab_c2 )
182 }
183
184 pub fn read_image( io : &InputOutput ) -> Result< image::DynamicImage >
186 {
187 let img = image::open( &io.input ).
188 map_err( | e | Error::ImageError( e ) )?;
189
190 Ok( img )
191 }
192
193 pub fn write_svg( io : &InputOutput, svg : &SvgFile ) -> Result< () >
207 {
208 let mut output_path =
209 match io.output
210 {
211 Some( ref o ) => o.clone(),
212 None => io.input.clone()
213 };
214 output_path.set_extension( "svg" );
215 let mut out = std::fs::File::create( output_path )
216 .map_err( | e | Error::IOError( e ) )?;
217
218 write!( &mut out, "{}", svg ).unwrap();
219 Ok( () )
220 }
221}
222
223
224crate::mod_interface!
225{
226
227 orphan use
228 {
229 find_unused_color_in_image,
230 color_exists_in_image,
231 should_key_image,
232 background_color,
233 euclid_difference,
234 read_image,
235 write_svg
236 };
237}