1use yscv_tensor::Tensor;
2
3use super::super::ImgProcError;
4use super::super::shape::hwc_shape;
5
6#[derive(Debug, Clone)]
10pub struct FarnebackConfig {
11 pub pyr_scale: f32,
13 pub levels: usize,
15 pub win_size: usize,
17 pub iterations: usize,
19 pub poly_n: usize,
21 pub poly_sigma: f32,
23}
24
25impl Default for FarnebackConfig {
26 fn default() -> Self {
27 Self {
28 pyr_scale: 0.5,
29 levels: 3,
30 win_size: 15,
31 iterations: 3,
32 poly_n: 5,
33 poly_sigma: 1.1,
34 }
35 }
36}
37
38pub fn farneback_flow(
43 prev: &Tensor,
44 next: &Tensor,
45 config: &FarnebackConfig,
46) -> Result<(Tensor, Tensor), ImgProcError> {
47 let shape = prev.shape();
48 if shape.len() != 2 {
49 return Err(ImgProcError::InvalidImageShape {
50 expected_rank: 2,
51 got: shape.to_vec(),
52 });
53 }
54 let (h, w) = (shape[0], shape[1]);
55 let next_shape = next.shape();
56 if next_shape.len() != 2 || next_shape[0] != h || next_shape[1] != w {
57 return Err(ImgProcError::ShapeMismatch {
58 expected: vec![h, w],
59 got: next_shape.to_vec(),
60 });
61 }
62
63 let levels = config.levels.max(1);
64 let pyr_scale = config.pyr_scale.clamp(0.1, 0.95);
65
66 let pyr_prev = build_pyramid(prev, levels, pyr_scale);
68 let pyr_next = build_pyramid(next, levels, pyr_scale);
69
70 let coarsest = &pyr_prev[levels - 1];
72 let mut ch = coarsest.shape()[0];
73 let mut cw = coarsest.shape()[1];
74 let mut flow_x = vec![0.0f32; ch * cw];
75 let mut flow_y = vec![0.0f32; ch * cw];
76
77 for level in (0..levels).rev() {
79 let lh = pyr_prev[level].shape()[0];
80 let lw = pyr_prev[level].shape()[1];
81
82 if level < levels - 1 {
83 let (ux, uy) = upsample_flow_vecs(&flow_x, &flow_y, ch, cw, lh, lw);
85 flow_x = ux;
86 flow_y = uy;
87 }
88
89 let prev_data = pyr_prev[level].data();
90 let next_data = pyr_next[level].data();
91
92 let half = (config.win_size / 2) as i32;
94 let sigma = config.win_size as f32 / 4.0;
95 let weights = gaussian_weights(half, sigma);
96
97 for _iter in 0..config.iterations {
98 let warped = warp_image_data(next_data, &flow_x, &flow_y, lh, lw);
100
101 let (grad_x, grad_y) = compute_gradients_data(prev_data, lh, lw);
103
104 let grad_t: Vec<f32> = warped
106 .iter()
107 .zip(prev_data.iter())
108 .map(|(w_val, p_val)| w_val - p_val)
109 .collect();
110
111 let mut new_fx = flow_x.clone();
113 let mut new_fy = flow_y.clone();
114
115 for y in 0..lh {
116 for x in 0..lw {
117 let mut sum_ixx = 0.0f32;
118 let mut sum_iyy = 0.0f32;
119 let mut sum_ixy = 0.0f32;
120 let mut sum_ixt = 0.0f32;
121 let mut sum_iyt = 0.0f32;
122
123 for dy in -half..=half {
124 for dx in -half..=half {
125 let sy = y as i32 + dy;
126 let sx = x as i32 + dx;
127 if sy < 0 || sy >= lh as i32 || sx < 0 || sx >= lw as i32 {
128 continue;
129 }
130 let idx = sy as usize * lw + sx as usize;
131 let wi = (dy + half) as usize * (2 * half as usize + 1)
132 + (dx + half) as usize;
133 let wt = weights[wi];
134 let ix = grad_x[idx];
135 let iy = grad_y[idx];
136 let it = grad_t[idx];
137 sum_ixx += wt * ix * ix;
138 sum_iyy += wt * iy * iy;
139 sum_ixy += wt * ix * iy;
140 sum_ixt += wt * ix * it;
141 sum_iyt += wt * iy * it;
142 }
143 }
144
145 let det = sum_ixx * sum_iyy - sum_ixy * sum_ixy;
146 if det.abs() > 1e-6 {
147 let inv_det = 1.0 / det;
148 let dvx = -(sum_iyy * sum_ixt - sum_ixy * sum_iyt) * inv_det;
149 let dvy = -(sum_ixx * sum_iyt - sum_ixy * sum_ixt) * inv_det;
150 let pidx = y * lw + x;
151 new_fx[pidx] += dvx;
152 new_fy[pidx] += dvy;
153 }
154 }
155 }
156
157 flow_x = new_fx;
158 flow_y = new_fy;
159 }
160
161 ch = lh;
162 cw = lw;
163 }
164
165 let fx_tensor = Tensor::from_vec(vec![h, w], flow_x)?;
166 let fy_tensor = Tensor::from_vec(vec![h, w], flow_y)?;
167 Ok((fx_tensor, fy_tensor))
168}
169
170fn compute_gradients_data(data: &[f32], h: usize, w: usize) -> (Vec<f32>, Vec<f32>) {
172 let mut dx = vec![0.0f32; h * w];
173 let mut dy = vec![0.0f32; h * w];
174 for y in 0..h {
175 for x in 0..w {
176 let idx = y * w + x;
177 dx[idx] = if x == 0 {
178 data[idx + 1] - data[idx]
179 } else if x == w - 1 {
180 data[idx] - data[idx - 1]
181 } else {
182 (data[idx + 1] - data[idx - 1]) * 0.5
183 };
184 dy[idx] = if y == 0 {
185 data[(y + 1) * w + x] - data[idx]
186 } else if y == h - 1 {
187 data[idx] - data[(y - 1) * w + x]
188 } else {
189 (data[(y + 1) * w + x] - data[(y - 1) * w + x]) * 0.5
190 };
191 }
192 }
193 (dx, dy)
194}
195
196fn warp_image_data(data: &[f32], flow_x: &[f32], flow_y: &[f32], h: usize, w: usize) -> Vec<f32> {
198 let mut out = vec![0.0f32; h * w];
199 for y in 0..h {
200 for x in 0..w {
201 let idx = y * w + x;
202 let src_x = x as f32 + flow_x[idx];
203 let src_y = y as f32 + flow_y[idx];
204 let sx = src_x.clamp(0.0, (w - 1) as f32);
205 let sy = src_y.clamp(0.0, (h - 1) as f32);
206 let x0 = sx.floor() as usize;
207 let y0 = sy.floor() as usize;
208 let x1 = (x0 + 1).min(w - 1);
209 let y1 = (y0 + 1).min(h - 1);
210 let fx = sx - x0 as f32;
211 let fy = sy - y0 as f32;
212 out[idx] = data[y0 * w + x0] * (1.0 - fy) * (1.0 - fx)
213 + data[y0 * w + x1] * (1.0 - fy) * fx
214 + data[y1 * w + x0] * fy * (1.0 - fx)
215 + data[y1 * w + x1] * fy * fx;
216 }
217 }
218 out
219}
220
221fn build_pyramid(image: &Tensor, levels: usize, scale: f32) -> Vec<Tensor> {
223 let mut pyramid = Vec::with_capacity(levels);
224 pyramid.push(image.clone());
225 for i in 1..levels {
226 let prev_level = &pyramid[i - 1];
227 let ph = prev_level.shape()[0];
228 let pw = prev_level.shape()[1];
229 let nh = ((ph as f32) * scale).round().max(1.0) as usize;
230 let nw = ((pw as f32) * scale).round().max(1.0) as usize;
231 let downscaled = downsample(prev_level.data(), ph, pw, nh, nw);
232 pyramid.push(
233 Tensor::from_vec(vec![nh, nw], downscaled).expect("pyramid level shape matches data"),
234 );
235 }
236 pyramid
237}
238
239fn downsample(data: &[f32], sh: usize, sw: usize, dh: usize, dw: usize) -> Vec<f32> {
241 let mut out = vec![0.0f32; dh * dw];
242 for y in 0..dh {
243 for x in 0..dw {
244 let src_y = (y as f32 + 0.5) * (sh as f32 / dh as f32) - 0.5;
245 let src_x = (x as f32 + 0.5) * (sw as f32 / dw as f32) - 0.5;
246 let sy = src_y.clamp(0.0, (sh - 1) as f32);
247 let sx = src_x.clamp(0.0, (sw - 1) as f32);
248 let y0 = sy.floor() as usize;
249 let x0 = sx.floor() as usize;
250 let y1 = (y0 + 1).min(sh - 1);
251 let x1 = (x0 + 1).min(sw - 1);
252 let fy = sy - y0 as f32;
253 let fx = sx - x0 as f32;
254 out[y * dw + x] = data[y0 * sw + x0] * (1.0 - fy) * (1.0 - fx)
255 + data[y0 * sw + x1] * (1.0 - fy) * fx
256 + data[y1 * sw + x0] * fy * (1.0 - fx)
257 + data[y1 * sw + x1] * fy * fx;
258 }
259 }
260 out
261}
262
263fn upsample_flow_vecs(
265 fx: &[f32],
266 fy: &[f32],
267 sh: usize,
268 sw: usize,
269 dh: usize,
270 dw: usize,
271) -> (Vec<f32>, Vec<f32>) {
272 let scale_x = dw as f32 / sw as f32;
273 let scale_y = dh as f32 / sh as f32;
274 let mut out_x = vec![0.0f32; dh * dw];
275 let mut out_y = vec![0.0f32; dh * dw];
276 for y in 0..dh {
277 for x in 0..dw {
278 let src_y = (y as f32 + 0.5) / scale_y - 0.5;
279 let src_x = (x as f32 + 0.5) / scale_x - 0.5;
280 let sy = src_y.clamp(0.0, (sh - 1) as f32);
281 let sx = src_x.clamp(0.0, (sw - 1) as f32);
282 let y0 = sy.floor() as usize;
283 let x0 = sx.floor() as usize;
284 let y1 = (y0 + 1).min(sh - 1);
285 let x1 = (x0 + 1).min(sw - 1);
286 let frac_y = sy - y0 as f32;
287 let frac_x = sx - x0 as f32;
288 let interp = |data: &[f32]| {
289 data[y0 * sw + x0] * (1.0 - frac_y) * (1.0 - frac_x)
290 + data[y0 * sw + x1] * (1.0 - frac_y) * frac_x
291 + data[y1 * sw + x0] * frac_y * (1.0 - frac_x)
292 + data[y1 * sw + x1] * frac_y * frac_x
293 };
294 let idx = y * dw + x;
295 out_x[idx] = interp(fx) * scale_x;
296 out_y[idx] = interp(fy) * scale_y;
297 }
298 }
299 (out_x, out_y)
300}
301
302fn gaussian_weights(half: i32, sigma: f32) -> Vec<f32> {
304 let size = (2 * half + 1) as usize;
305 let mut w = vec![0.0f32; size * size];
306 let mut sum = 0.0f32;
307 let s2 = 2.0 * sigma * sigma;
308 for dy in -half..=half {
309 for dx in -half..=half {
310 let val = (-(dx * dx + dy * dy) as f32 / s2).exp();
311 let idx = (dy + half) as usize * size + (dx + half) as usize;
312 w[idx] = val;
313 sum += val;
314 }
315 }
316 if sum > 0.0 {
317 for v in &mut w {
318 *v /= sum;
319 }
320 }
321 w
322}
323
324pub fn lucas_kanade_optical_flow(
328 prev: &Tensor,
329 next: &Tensor,
330 points: &[(usize, usize)],
331 window_size: usize,
332) -> Result<Vec<(f32, f32)>, ImgProcError> {
333 let (h, w, c) = hwc_shape(prev)?;
334 if c != 1 {
335 return Err(ImgProcError::InvalidChannelCount {
336 expected: 1,
337 got: c,
338 });
339 }
340 let (h2, w2, c2) = hwc_shape(next)?;
341 if h != h2 || w != w2 || c2 != 1 {
342 return Err(ImgProcError::InvalidSize {
343 height: h2,
344 width: w2,
345 });
346 }
347 let prev_d = prev.data();
348 let next_d = next.data();
349 let half = (window_size / 2) as i32;
350 let mut flows = Vec::with_capacity(points.len());
351 for &(px, py) in points {
352 let mut ixx = 0.0f32;
353 let mut iyy = 0.0f32;
354 let mut ixy = 0.0f32;
355 let mut ixt = 0.0f32;
356 let mut iyt = 0.0f32;
357 for dy in -half..=half {
358 for dx in -half..=half {
359 let y = py as i32 + dy;
360 let x = px as i32 + dx;
361 if y < 1 || y >= (h as i32 - 1) || x < 1 || x >= (w as i32 - 1) {
362 continue;
363 }
364 let (yu, xu) = (y as usize, x as usize);
365 let ix = (prev_d[yu * w + xu + 1] - prev_d[yu * w + xu - 1]) * 0.5;
366 let iy = (prev_d[(yu + 1) * w + xu] - prev_d[(yu - 1) * w + xu]) * 0.5;
367 let it = next_d[yu * w + xu] - prev_d[yu * w + xu];
368 ixx += ix * ix;
369 iyy += iy * iy;
370 ixy += ix * iy;
371 ixt += ix * it;
372 iyt += iy * it;
373 }
374 }
375 let det = ixx * iyy - ixy * ixy;
376 if det.abs() < 1e-6 {
377 flows.push((0.0, 0.0));
378 } else {
379 let vx = -(iyy * ixt - ixy * iyt) / det;
380 let vy = -(ixx * iyt - ixy * ixt) / det;
381 flows.push((vx, vy));
382 }
383 }
384 Ok(flows)
385}
386
387pub fn watershed(image: &Tensor, markers: &Tensor) -> Result<Tensor, ImgProcError> {
392 let (h, w, c) = hwc_shape(image)?;
393 if c != 1 {
394 return Err(ImgProcError::InvalidChannelCount {
395 expected: 1,
396 got: c,
397 });
398 }
399 let (mh, mw, mc) = hwc_shape(markers)?;
400 if mh != h || mw != w || mc != 1 {
401 return Err(ImgProcError::InvalidSize {
402 height: mh,
403 width: mw,
404 });
405 }
406 let img = image.data();
407 let mark = markers.data();
408 let mut labels = vec![0i32; h * w];
409 let mut queue: std::collections::BinaryHeap<std::cmp::Reverse<(u32, usize)>> =
410 std::collections::BinaryHeap::new();
411 let nbr: [(i32, i32); 4] = [(-1, 0), (1, 0), (0, -1), (0, 1)];
412 for i in 0..h * w {
413 let m = mark[i] as i32;
414 if m > 0 {
415 labels[i] = m;
416 let (y, x) = (i / w, i % w);
417 for &(dy, dx) in &nbr {
418 let ny = y as i32 + dy;
419 let nx = x as i32 + dx;
420 if ny >= 0 && ny < h as i32 && nx >= 0 && nx < w as i32 {
421 let ni = ny as usize * w + nx as usize;
422 if labels[ni] == 0 && mark[ni] as i32 == 0 {
423 queue.push(std::cmp::Reverse(((img[ni] * 65535.0) as u32, ni)));
424 }
425 }
426 }
427 }
428 }
429 while let Some(std::cmp::Reverse((_, idx))) = queue.pop() {
430 if labels[idx] != 0 {
431 continue;
432 }
433 let (y, x) = (idx / w, idx % w);
434 let mut label = 0i32;
435 for &(dy, dx) in &nbr {
436 let ny = y as i32 + dy;
437 let nx = x as i32 + dx;
438 if ny >= 0 && ny < h as i32 && nx >= 0 && nx < w as i32 {
439 let ni = ny as usize * w + nx as usize;
440 if labels[ni] > 0 {
441 label = labels[ni];
442 break;
443 }
444 }
445 }
446 if label == 0 {
447 continue;
448 }
449 labels[idx] = label;
450 for &(dy, dx) in &nbr {
451 let ny = y as i32 + dy;
452 let nx = x as i32 + dx;
453 if ny >= 0 && ny < h as i32 && nx >= 0 && nx < w as i32 {
454 let ni = ny as usize * w + nx as usize;
455 if labels[ni] == 0 {
456 queue.push(std::cmp::Reverse(((img[ni] * 65535.0) as u32, ni)));
457 }
458 }
459 }
460 }
461 let out_data: Vec<f32> = labels.iter().map(|&l| l as f32).collect();
462 Tensor::from_vec(vec![h, w, 1], out_data).map_err(Into::into)
463}
464
465pub fn dense_optical_flow(
472 prev: &Tensor,
473 next: &Tensor,
474 window_size: usize,
475 num_iterations: usize,
476) -> Result<Tensor, ImgProcError> {
477 let (h, w, c) = hwc_shape(prev)?;
478 if c != 1 {
479 return Err(ImgProcError::InvalidChannelCount {
480 expected: 1,
481 got: c,
482 });
483 }
484 let (h2, w2, c2) = hwc_shape(next)?;
485 if h != h2 || w != w2 || c2 != 1 {
486 return Err(ImgProcError::InvalidSize {
487 height: h2,
488 width: w2,
489 });
490 }
491
492 let half = (window_size / 2) as i32;
493 let prev_d = prev.data();
494 let next_d = next.data();
495
496 let mut flow_x = vec![0.0f32; h * w];
497 let mut flow_y = vec![0.0f32; h * w];
498
499 for _iter in 0..num_iterations {
500 for y in 0..h {
501 for x in 0..w {
502 let mut sum_ixx = 0.0f32;
503 let mut sum_iyy = 0.0f32;
504 let mut sum_ixy = 0.0f32;
505 let mut sum_ixt = 0.0f32;
506 let mut sum_iyt = 0.0f32;
507
508 for dy in -half..=half {
509 for dx in -half..=half {
510 let sy = y as i32 + dy;
511 let sx = x as i32 + dx;
512 if sy < 1 || sy >= (h as i32 - 1) || sx < 1 || sx >= (w as i32 - 1) {
513 continue;
514 }
515 let su = sy as usize;
516 let sxu = sx as usize;
517 let ix = (prev_d[su * w + sxu + 1] - prev_d[su * w + sxu - 1]) * 0.5;
518 let iy = (prev_d[(su + 1) * w + sxu] - prev_d[(su - 1) * w + sxu]) * 0.5;
519
520 let warped_y = sy as f32 + flow_y[y * w + x];
521 let warped_x = sx as f32 + flow_x[y * w + x];
522 let wy = warped_y.clamp(0.0, (h - 1) as f32);
523 let wx = warped_x.clamp(0.0, (w - 1) as f32);
524 let wy0 = wy.floor() as usize;
525 let wx0 = wx.floor() as usize;
526 let wy1 = (wy0 + 1).min(h - 1);
527 let wx1 = (wx0 + 1).min(w - 1);
528 let fy = wy - wy0 as f32;
529 let fx = wx - wx0 as f32;
530 let warped_val = next_d[wy0 * w + wx0] * (1.0 - fy) * (1.0 - fx)
531 + next_d[wy0 * w + wx1] * (1.0 - fy) * fx
532 + next_d[wy1 * w + wx0] * fy * (1.0 - fx)
533 + next_d[wy1 * w + wx1] * fy * fx;
534 let it = warped_val - prev_d[su * w + sxu];
535
536 sum_ixx += ix * ix;
537 sum_iyy += iy * iy;
538 sum_ixy += ix * iy;
539 sum_ixt += ix * it;
540 sum_iyt += iy * it;
541 }
542 }
543
544 let det = sum_ixx * sum_iyy - sum_ixy * sum_ixy;
545 if det.abs() > 1e-6 {
546 flow_x[y * w + x] += -(sum_iyy * sum_ixt - sum_ixy * sum_iyt) / det;
547 flow_y[y * w + x] += -(sum_ixx * sum_iyt - sum_ixy * sum_ixt) / det;
548 }
549 }
550 }
551 }
552
553 let mut out = Vec::with_capacity(h * w * 2);
554 for y in 0..h {
555 for x in 0..w {
556 out.push(flow_x[y * w + x]);
557 out.push(flow_y[y * w + x]);
558 }
559 }
560 Tensor::from_vec(vec![h, w, 2], out).map_err(Into::into)
561}