1
0

lib.rs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. use image::{GrayImage, Luma, imageops::FilterType::Nearest};
  2. use svg::{Document, node::element::Path, node::element::path::Data, node::element::Rectangle};
  3. /** Import a picture, resize it, and convert it to Greyscale */
  4. fn import_picture(path: &String, new_size: [u32; 2], edge_detection: bool) -> GrayImage {
  5. let src_img = image::open(path)
  6. .expect("failed to read image")
  7. .resize(new_size[0], new_size[1], Nearest)
  8. .to_luma8();
  9. let img: GrayImage = if edge_detection {
  10. let detection = edge_detection::canny(src_img, 1.8, 0.1, 0.01);
  11. detection.as_image()
  12. .to_luma8()
  13. } else {
  14. src_img
  15. };
  16. img
  17. }
  18. /** Create a white image in Greyscale */
  19. fn create_blank_image(width: u32, height: u32) -> GrayImage {
  20. GrayImage::from_pixel(width, height, Luma([255]))
  21. }
  22. /** Reduce the value of the given pixel by 51 (cause it's 255 / 5, so it makes 6 levels of grey) */
  23. fn darken_pixel(mut img: GrayImage, x: u32, y: u32) -> GrayImage {
  24. let px_val: u8 = img.get_pixel(x, y).0[0];
  25. if 55 <= px_val {
  26. img.put_pixel(x, y, Luma([px_val - 55]));
  27. img
  28. } else {
  29. img
  30. }
  31. }
  32. /** Places the nails randomly on the image, with a higher probability as the pixel is white */
  33. fn create_nails_list(img: GrayImage, nails_count: u16, width: u32) -> Vec<[u32; 2]> {
  34. let mut vec_res: Vec<[u32; 2]> = vec![];
  35. let cumulative_pixel_values: Vec<u32> = image_pixels_cumsum(img.clone());
  36. for _ in 0..nails_count {
  37. let rd: u32 = fastrand::u32(0..cumulative_pixel_values[cumulative_pixel_values.len() - 1]);
  38. let px_ind: u32 = find_pixel(&cumulative_pixel_values, rd);
  39. let nail: [u32; 2] = [px_ind % width, px_ind / width];
  40. vec_res.push(nail);
  41. }
  42. vec_res
  43. }
  44. /** Takes an image and return the cumulative sum of its pixels in a vector */
  45. fn image_pixels_cumsum(img: GrayImage) -> Vec<u32> {
  46. img.into_vec()
  47. .into_iter()
  48. .scan(0, |acc, x| {
  49. *acc += u32::from(x);
  50. Some(*acc)
  51. })
  52. .collect()
  53. }
  54. /** Takes a cumulative sum of a vector's valeus and a number, and return the index where the number is bigger than the sum */
  55. fn find_pixel(cumsum: &Vec<u32>, nb: u32) -> u32 {
  56. let mut i: usize = 0;
  57. while cumsum[i] < nb {
  58. i += 1;
  59. }
  60. i as u32
  61. }
  62. /** Draws a line on the given image between the two given points */
  63. fn draw_line(mut img: GrayImage, nail1: [u32; 2], nail2: [u32; 2]) -> GrayImage {
  64. let px_arr: Vec<[u32; 2]> = pixels_on_the_line(nail1, nail2);
  65. for i in 0..px_arr.len() {
  66. img = darken_pixel(img, px_arr[i][0], px_arr[i][1]);
  67. }
  68. img
  69. }
  70. /** Returns a vector with all the points through which the line passes */
  71. fn pixels_on_the_line(start: [u32; 2], end: [u32; 2]) -> Vec<[u32; 2]> {
  72. let mut vec_res: Vec<[u32; 2]> = vec![];
  73. let eq_vals: [f32; 2] = line_equation_values(start, end);
  74. let a: f32 = eq_vals[0];
  75. let b: f32 = eq_vals[1];
  76. let mut x: f32;
  77. let mut y: f32;
  78. if -1.0 <= a && a <= 1.0 {
  79. let x2: f32;
  80. if start[0] < end[0] {
  81. x = (start[0] + 1) as f32;
  82. x2 = end[0] as f32;
  83. vec_res.push([start[0], start[1]]);
  84. } else {
  85. x = (end[0] + 1) as f32;
  86. x2 = start[0] as f32;
  87. vec_res.push([end[0], end[1]]);
  88. }
  89. while x <= x2 {
  90. y = a * x + b;
  91. vec_res.push([x as u32, y as u32]);
  92. x += 1.;
  93. }
  94. } else {
  95. let y2: f32;
  96. if start[1] < end[1] {
  97. x = start[0] as f32;
  98. y2 = end[1] as f32;
  99. y = (start[1] + 1) as f32;
  100. vec_res.push([start[0], start[1]]);
  101. } else {
  102. x = end[0] as f32;
  103. y2 = start[1] as f32;
  104. y = (end[1] + 1) as f32;
  105. vec_res.push([end[0], end[1]]);
  106. }
  107. if a != 999999999. && b != 999999999. {
  108. while y <= y2 {
  109. x = (y - b) / a;
  110. vec_res.push([x as u32, y as u32]);
  111. y += 1.;
  112. }
  113. } else {
  114. for i in y as u32..(y2 + 1.) as u32 {
  115. vec_res.push([x as u32, i]);
  116. }
  117. }
  118. }
  119. vec_res
  120. }
  121. /** Calculates the variablees of the line's equation */
  122. fn line_equation_values(start: [u32; 2], end: [u32; 2]) -> [f32; 2] {
  123. let a: f32;
  124. let b: f32;
  125. if start[0] != end[0] {
  126. a = (i64::from(end[1]) - i64::from(start[1])) as f32 / (i64::from(end[0]) - i64::from(start[0])) as f32;
  127. b = start[1] as f32 - i64::from(start[0]) as f32 * a;
  128. } else {
  129. a = 999999999.;
  130. b = 999999999.;
  131. }
  132. [a, b]
  133. }
  134. /** Calculates the reduction of the error if the line is drawn */
  135. fn line_error_reduction(start: [u32; 2], end: [u32; 2], img_src: &GrayImage, img: &GrayImage) -> f32 {
  136. if start == end {
  137. return 999.
  138. }
  139. let px_arr = pixels_on_the_line(start, end);
  140. let mut err: f32 = 0.;
  141. let px_cnt: f32 = px_arr.len() as f32;
  142. for i in 0..px_cnt as usize {
  143. err += pixel_error(px_arr[i], img_src, img, false);
  144. }
  145. let mut new_err: f32 = 0.;
  146. for i in 0..px_cnt as usize {
  147. new_err += pixel_error(px_arr[i], img_src, img, true);
  148. }
  149. (new_err - err) / px_cnt
  150. }
  151. /** Returns the absolute value of the difference between the same pixel on the two images */
  152. fn pixel_error(px: [u32; 2], img_src: &GrayImage, img: &GrayImage, darkened: bool) -> f32 {
  153. if darkened {
  154. if img.get_pixel(px[0], px[1]).0[0] >= 51 {
  155. (i32::from(img_src.get_pixel(px[0], px[1]).0[0]) - i32::from(img.get_pixel(px[0], px[1]).0[0]) + 51).pow(2) as f32
  156. } else {
  157. pixel_error(px, img_src, img, false)
  158. }
  159. } else {
  160. (i32::from(img_src.get_pixel(px[0], px[1]).0[0]) - i32::from(img.get_pixel(px[0], px[1]).0[0])).pow(2) as f32
  161. }
  162. }
  163. /** Finds the line that causes the greatest error reduction between the images */
  164. fn find_best_line(img_src: &GrayImage, img: &GrayImage, start: [u32; 2], nails: Vec<[u32; 2]>) -> ([u32; 2], f32) {
  165. let half_nails_cnt: usize = nails.len() / 2;
  166. let half_nails: Vec<[u32; 2]> = fastrand::choose_multiple(nails, half_nails_cnt);
  167. let mut err_red_vec: Vec<f32> = vec![];
  168. for i in 0..half_nails.len() {
  169. err_red_vec.push(line_error_reduction(start, half_nails[i], &img_src, &img));
  170. }
  171. let val_and_ind_best_err_red: (f32, usize) = minimum_value_and_index_f32_vector(&err_red_vec);
  172. let best_err_red: f32 = val_and_ind_best_err_red.0;
  173. let ind_best_err_red: usize = val_and_ind_best_err_red.1;
  174. let best_nail: [u32; 2] = half_nails[ind_best_err_red];
  175. (best_nail, best_err_red)
  176. }
  177. /** Returns the index of the minimum element in the given vector */
  178. fn minimum_value_and_index_f32_vector(vec: &Vec<f32>) -> (f32, usize) {
  179. let mut min: f32 = vec[0];
  180. let mut min_ind: usize = 0;
  181. for i in 1..vec.len() {
  182. if vec[i] < min {
  183. min = vec[i];
  184. min_ind = i;
  185. }
  186. }
  187. (min, min_ind)
  188. }
  189. /** Main function */
  190. pub fn p2sa(src_path: String, output_path: String, new_size: [u32; 2], nails_count: u16) {
  191. let src_img: GrayImage = import_picture(&src_path, new_size, false);
  192. let mut new_img: GrayImage = create_blank_image(new_size[0], new_size[1]);
  193. let edges_img: GrayImage = import_picture(&src_path, new_size, true);
  194. let nails: Vec<[u32; 2]> = create_nails_list(edges_img, nails_count, new_size[0]);
  195. let mut start: [u32; 2] = nails[0];
  196. let mut err_rising_cnt: u8 = 0;
  197. let mut lines_cnt: u16 = 0;
  198. let mut first_err_red: f32 = 0.;
  199. let is_svg: bool;
  200. let mut svg_data: Data = Data::new();
  201. if &output_path[output_path.len() - 4..output_path.len()] == ".svg" {
  202. is_svg = true;
  203. svg_data = svg_data
  204. .move_to((start[0], start[1]));
  205. } else {
  206. is_svg = false;
  207. }
  208. while err_rising_cnt < 3 {
  209. let best_nail_and_err_red: ([u32; 2], f32) = find_best_line(&src_img, &new_img, start, nails.clone());
  210. let best_nail: [u32; 2] = best_nail_and_err_red.0;
  211. if lines_cnt == 0 {
  212. first_err_red = best_nail_and_err_red.1;
  213. }
  214. println!("Progress estimation : {} %", (best_nail_and_err_red.1 - first_err_red) * 100. / - first_err_red);
  215. if best_nail_and_err_red.1 >= 0. {
  216. err_rising_cnt += 1;
  217. } else {
  218. err_rising_cnt = 0;
  219. }
  220. new_img = draw_line(new_img, start, best_nail);
  221. if is_svg {
  222. svg_data = svg_data.line_to((start[0], start[1]));
  223. }
  224. start = best_nail;
  225. lines_cnt += 1;
  226. }
  227. svg_data = svg_data.close();
  228. if is_svg {
  229. let svg_bg = Rectangle::new()
  230. .set("width", new_size[0])
  231. .set("height", new_size[1])
  232. .set("x", 0)
  233. .set("y", 0)
  234. .set("fill", "white")
  235. .set("stroke", "none");
  236. let svg_path = Path::new()
  237. .set("fill", "none")
  238. .set("stroke", "black")
  239. .set("stroke-width", 1)
  240. .set("stroke-opacity", 0.2)
  241. .set("d", svg_data);
  242. let svg_document = Document::new()
  243. .set("viewbox", (0, 0, new_size[0], new_size[1]))
  244. .set("width", new_size[0])
  245. .set("height", new_size[1])
  246. .add(svg_bg)
  247. .add(svg_path);
  248. let _ = svg::save(&output_path, &svg_document);
  249. } else {
  250. let _ = new_img.save(output_path);
  251. }
  252. println!("{} lines drawn", lines_cnt);
  253. }