1#[cfg(feature = "debug-rect")]
26use std::sync::Mutex;
27
28#[cfg(feature = "debug-rect")]
32#[doc(hidden)]
33pub static LOG: Mutex<Vec<String>> = Mutex::new(Vec::new());
34
35#[cfg(feature = "debug-rect")]
44#[macro_export]
45macro_rules! debug_rect {
46 ($stage:expr, $x:expr, $y:expr, $w:expr, $h:expr, $($arg:tt)*) => {{
47 let msg = format!($($arg)*);
48 let row = format!("{},{},{},{},{},{}", $stage, $x, $y, $w, $h, msg.replace(',', ";"));
49 if let Ok(mut buf) = $crate::debug_rect::LOG.lock() {
50 buf.push(row);
51 }
52 }};
53}
54
55#[cfg(not(feature = "debug-rect"))]
58#[macro_export]
59macro_rules! debug_rect {
60 ($stage:expr, $x:expr, $y:expr, $w:expr, $h:expr, $($arg:tt)*) => {
61 if false {
62 let _ = ($stage, $x, $y, $w, $h);
64 let _ = format_args!($($arg)*);
65 }
66 };
67}
68
69#[cfg(feature = "debug-rect")]
71pub fn clear() {
72 if let Ok(mut buf) = LOG.lock() {
73 buf.clear();
74 }
75}
76
77#[cfg(not(feature = "debug-rect"))]
78pub fn clear() {}
79
80#[cfg(feature = "debug-rect")]
85pub fn flush(base_path: &str) {
86 let path = if base_path.is_empty() {
87 "debug_rect.csv".to_string()
88 } else {
89 format!("{base_path}.debug_rect.csv")
90 };
91 let rows = {
92 let Ok(buf) = LOG.lock() else { return };
93 buf.clone()
94 };
95 if rows.is_empty() {
96 return;
97 }
98 let mut out = String::with_capacity(rows.len() * 80);
99 out.push_str("stage,x,y,w,h,message\n");
100 for row in &rows {
101 out.push_str(row);
102 out.push('\n');
103 }
104 if let Err(e) = std::fs::write(&path, &out) {
105 eprintln!("debug_rect: failed to write {path}: {e}");
106 } else {
107 eprintln!("debug_rect: wrote {} rows to {path}", rows.len());
108 }
109}
110
111#[cfg(not(feature = "debug-rect"))]
112pub fn flush(_base_path: &str) {}
113
114#[cfg(feature = "debug-rect")]
119pub fn query_overlapping(qx: i64, qy: i64, qw: i64, qh: i64) -> Vec<String> {
120 let Ok(buf) = LOG.lock() else {
121 return Vec::new();
122 };
123 let mut hits = Vec::new();
124 for row in buf.iter() {
125 let parts: Vec<&str> = row.splitn(6, ',').collect();
127 if parts.len() < 5 {
128 continue;
129 }
130 let Ok(rx) = parts[1].parse::<i64>() else {
131 continue;
132 };
133 let Ok(ry) = parts[2].parse::<i64>() else {
134 continue;
135 };
136 let Ok(rw) = parts[3].parse::<i64>() else {
137 continue;
138 };
139 let Ok(rh) = parts[4].parse::<i64>() else {
140 continue;
141 };
142 if rx < qx + qw && rx + rw > qx && ry < qy + qh && ry + rh > qy {
144 hits.push(row.clone());
145 }
146 }
147 hits
148}
149
150#[cfg(not(feature = "debug-rect"))]
151pub fn query_overlapping(_qx: i64, _qy: i64, _qw: i64, _qh: i64) -> Vec<String> {
152 Vec::new()
153}
154
155#[cfg(feature = "debug-rect")]
163pub fn find_worst_block(
164 img_a: &[u8],
165 img_b: &[u8],
166 width: usize,
167 height: usize,
168 channels: usize,
169 block_size: usize,
170) -> (usize, usize, usize, usize, f64) {
171 assert_eq!(img_a.len(), img_b.len());
172 assert!(img_a.len() >= width * height * channels);
173 assert!(block_size > 0);
174
175 let mut worst_x = 0;
176 let mut worst_y = 0;
177 let mut worst_sad = 0.0_f64;
178
179 let mut by = 0;
180 while by < height {
181 let bh = block_size.min(height - by);
182 let mut bx = 0;
183 while bx < width {
184 let bw = block_size.min(width - bx);
185 let mut sad = 0.0_f64;
186 for dy in 0..bh {
187 let row = (by + dy) * width * channels + bx * channels;
188 for dx_c in 0..(bw * channels) {
189 let a = img_a[row + dx_c] as f64;
190 let b = img_b[row + dx_c] as f64;
191 sad += (a - b).abs();
192 }
193 }
194 if sad > worst_sad {
195 worst_sad = sad;
196 worst_x = bx;
197 worst_y = by;
198 }
199 bx += block_size;
200 }
201 by += block_size;
202 }
203
204 let final_w = block_size.min(width - worst_x);
205 let final_h = block_size.min(height - worst_y);
206 (worst_x, worst_y, final_w, final_h, worst_sad)
207}
208
209#[cfg(not(feature = "debug-rect"))]
210pub fn find_worst_block(
211 _img_a: &[u8],
212 _img_b: &[u8],
213 _width: usize,
214 _height: usize,
215 _channels: usize,
216 _block_size: usize,
217) -> (usize, usize, usize, usize, f64) {
218 (0, 0, 0, 0, 0.0)
219}
220
221#[cfg(feature = "debug-rect")]
228pub fn diff_and_query(
229 img_a: &[u8],
230 img_b: &[u8],
231 width: usize,
232 height: usize,
233 channels: usize,
234 block_size: usize,
235) -> (usize, usize, usize, usize, f64, Vec<String>) {
236 let (x, y, w, h, sad) = find_worst_block(img_a, img_b, width, height, channels, block_size);
237 let rows = query_overlapping(x as i64, y as i64, w as i64, h as i64);
238 (x, y, w, h, sad, rows)
239}
240
241#[cfg(not(feature = "debug-rect"))]
242pub fn diff_and_query(
243 _img_a: &[u8],
244 _img_b: &[u8],
245 _width: usize,
246 _height: usize,
247 _channels: usize,
248 _block_size: usize,
249) -> (usize, usize, usize, usize, f64, Vec<String>) {
250 (0, 0, 0, 0, 0.0, Vec::new())
251}
252
253#[cfg(feature = "debug-rect")]
257pub fn find_worst_blocks(
258 img_a: &[u8],
259 img_b: &[u8],
260 width: usize,
261 height: usize,
262 channels: usize,
263 block_size: usize,
264 top_n: usize,
265) -> Vec<(usize, usize, usize, usize, f64)> {
266 assert_eq!(img_a.len(), img_b.len());
267 assert!(img_a.len() >= width * height * channels);
268 assert!(block_size > 0);
269
270 let mut blocks: Vec<(usize, usize, usize, usize, f64)> = Vec::new();
271
272 let mut by = 0;
273 while by < height {
274 let bh = block_size.min(height - by);
275 let mut bx = 0;
276 while bx < width {
277 let bw = block_size.min(width - bx);
278 let mut sad = 0.0_f64;
279 for dy in 0..bh {
280 let row = (by + dy) * width * channels + bx * channels;
281 for dx_c in 0..(bw * channels) {
282 let a = img_a[row + dx_c] as f64;
283 let b = img_b[row + dx_c] as f64;
284 sad += (a - b).abs();
285 }
286 }
287 blocks.push((bx, by, bw, bh, sad));
288 bx += block_size;
289 }
290 by += block_size;
291 }
292
293 blocks.sort_by(|a, b| b.4.partial_cmp(&a.4).unwrap_or(core::cmp::Ordering::Equal));
294 blocks.truncate(top_n);
295 blocks
296}
297
298#[cfg(not(feature = "debug-rect"))]
299pub fn find_worst_blocks(
300 _img_a: &[u8],
301 _img_b: &[u8],
302 _width: usize,
303 _height: usize,
304 _channels: usize,
305 _block_size: usize,
306 _top_n: usize,
307) -> Vec<(usize, usize, usize, usize, f64)> {
308 Vec::new()
309}