lib.rs 8.1 KB

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