1use image::{GrayImage, Luma};
2use crate::transform::perspective::PerspectiveTransform;
3
4pub fn warp_perspective(src_img: &GrayImage, pt: &PerspectiveTransform, width: u32, height: u32) -> GrayImage {
9 let mut dst = GrayImage::new(width, height);
10 let src_w = src_img.width() as f32;
11 let src_h = src_img.height() as f32;
12
13 for y in 0..height {
14 for x in 0..width {
15 let src_pt = pt.transform_inverse(x as f32, y as f32);
16 let sx = src_pt.x;
17 let sy = src_pt.y;
18
19 if sx >= 0.0 && sx < src_w - 1.0 && sy >= 0.0 && sy < src_h - 1.0 {
20 let x0 = sx.floor() as u32;
22 let y0 = sy.floor() as u32;
23
24 let dx = sx - x0 as f32;
25 let dy = sy - y0 as f32;
26
27 let p00 = src_img.get_pixel(x0, y0)[0] as f32;
28 let p10 = src_img.get_pixel(x0 + 1, y0)[0] as f32;
29 let p01 = src_img.get_pixel(x0, y0 + 1)[0] as f32;
30 let p11 = src_img.get_pixel(x0 + 1, y0 + 1)[0] as f32;
31
32 let val = (1.0 - dy) * ((1.0 - dx) * p00 + dx * p10)
33 + dy * ((1.0 - dx) * p01 + dx * p11);
34
35 dst.put_pixel(x, y, Luma([val as u8]));
36 }
37 }
38 }
39
40 dst
41}
42
43pub fn generate_marked_image(
46 gray: &image::GrayImage,
47 quad: &[crate::Point],
48 anchors: Option<&[crate::AnchorConfig]>,
49 marker_outlines: Option<&[Vec<crate::Point>]>,
50 transform: Option<&crate::transform::perspective::PerspectiveTransform>
51) -> String {
52 use imageproc::drawing::draw_line_segment_mut;
53 use image::Rgb;
54 use base64::Engine as _;
55
56 let (w, h) = gray.dimensions();
57 let mut rgb = image::RgbImage::new(w, h);
58 for y in 0..h {
59 for x in 0..w {
60 let v = gray.get_pixel(x, y)[0];
61 rgb.put_pixel(x, y, Rgb([v, v, v]));
62 }
63 }
64
65 let red = Rgb([255u8, 0, 0]);
66 let thickness = (w as f32 * 0.003).max(3.0) as i32;
67
68 for t in -(thickness / 2)..(thickness / 2) {
69 let offset = t as f32;
70 for i in 0..4 {
71 let p1 = quad[i];
72 let p2 = quad[(i + 1) % 4];
73 draw_line_segment_mut(&mut rgb, (p1.x + offset, p1.y), (p2.x + offset, p2.y), red);
74 draw_line_segment_mut(&mut rgb, (p1.x, p1.y + offset), (p2.x, p2.y + offset), red);
75 }
76 }
77
78 if let Some(anchs) = anchors {
80 let purple = Rgb([200u8, 0, 200]); use imageproc::drawing::{draw_hollow_rect_mut, draw_line_segment_mut};
82 use imageproc::rect::Rect;
83
84 for anchor in anchs {
85 if let Some(pt) = transform {
86 let p_tl = pt.transform_inverse(anchor.x, anchor.y);
88 let p_tr = pt.transform_inverse(anchor.x + anchor.width, anchor.y);
89 let p_br = pt.transform_inverse(anchor.x + anchor.width, anchor.y + anchor.height);
90 let p_bl = pt.transform_inverse(anchor.x, anchor.y + anchor.height);
91
92 let pts = [p_tl, p_tr, p_br, p_bl];
93 for t in -(thickness / 2)..(thickness / 2) {
94 let offset = t as f32;
95 for i in 0..4 {
96 let p1 = pts[i];
97 let p2 = pts[(i + 1) % 4];
98 draw_line_segment_mut(&mut rgb, (p1.x + offset, p1.y), (p2.x + offset, p2.y), purple);
99 draw_line_segment_mut(&mut rgb, (p1.x, p1.y + offset), (p2.x, p2.y + offset), purple);
100 }
101 }
102 } else {
103 let scale = w as f32 / 100.0;
105 let ax = anchor.x * scale;
106 let ay = anchor.y * scale;
107 let aw = anchor.width * scale;
108 let ah = anchor.height * scale;
109
110 for t in 0..thickness {
111 let rect = Rect::at((ax - t as f32/2.0) as i32, (ay - t as f32/2.0) as i32)
112 .of_size((aw + t as f32) as u32, (ah + t as f32) as u32);
113 draw_hollow_rect_mut(&mut rgb, rect, purple);
114 }
115 }
116 }
117 }
118
119 if let Some(outlines) = marker_outlines {
121 let green = Rgb([0u8, 255, 0]);
122 for outline in outlines {
123 for i in 0..outline.len() {
124 let p1 = outline[i];
125 let p2 = outline[(i + 1) % outline.len()];
126 draw_line_segment_mut(&mut rgb, (p1.x, p1.y), (p2.x, p2.y), green);
127 }
128 }
129 }
130
131 let mut buffer = std::io::Cursor::new(Vec::new());
132 let encoder = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut buffer, 85);
133 image::ImageEncoder::write_image(
134 encoder, rgb.as_raw(), w, h, image::ExtendedColorType::Rgb8
135 ).ok();
136
137 base64::engine::general_purpose::STANDARD.encode(buffer.get_ref())
138}
139
140pub fn to_png_bytes(img: &GrayImage) -> anyhow::Result<Vec<u8>> {
141 let mut buffer = std::io::Cursor::new(Vec::new());
142 img.write_to(&mut buffer, image::ImageFormat::Png)?;
143 Ok(buffer.into_inner())
144}
145
146pub fn encode_debug_image(img: &GrayImage) -> Option<String> {
147 use base64::Engine as _;
148 to_png_bytes(img).ok().map(|b| base64::engine::general_purpose::STANDARD.encode(b))
149}