depth_map_processor/
lib.rs1use colorgrad::Gradient;
2use image::{ImageBuffer, Luma, RgbImage};
3use std::path::Path;
4
5pub struct DepthMapStats {
7 pub width: u32,
8 pub height: u32,
9 pub min_val: u16,
10 pub max_val: u16,
11 pub sample_depth_m: f32,
12}
13
14pub fn process_depth_map(
17 input_path: &Path,
18 output_path: &Path,
19 viz_path: Option<&Path>,
20) -> Result<DepthMapStats, Box<dyn std::error::Error>> {
21 let img = image::open(input_path)
24 .map_err(|_| {
25 format!(
26 "Could not open or find the image at: {}",
27 input_path.display()
28 )
29 })?
30 .into_luma16();
31
32 let (width, height) = img.dimensions();
33
34 let pixel_val = img.get_pixel(0, 0)[0];
36 let depth_in_meters = pixel_val as f32 / 1000.0;
37
38 let (min_val, max_val) = img.pixels().fold((u16::MAX, u16::MIN), |(min, max), p| {
41 (min.min(p[0]), max.max(p[0]))
42 });
43
44 let mut norm_img: ImageBuffer<Luma<u8>, Vec<u8>> = ImageBuffer::new(width, height);
46
47 let range = (max_val - min_val) as f32;
49
50 for (x, y, pixel) in img.enumerate_pixels() {
51 let val = pixel[0];
52 let normalized = if range > 0.0 {
53 ((val as f32 - min_val as f32) / range * 255.0) as u8
54 } else {
55 0
56 };
57 norm_img.put_pixel(x, y, Luma([normalized]));
58 }
59
60 let grad = colorgrad::preset::turbo();
62 let mut colored_img: RgbImage = ImageBuffer::new(width, height);
63
64 for (x, y, pixel) in norm_img.enumerate_pixels() {
65 let t = pixel[0] as f32 / 255.0f32;
67 let rgba = grad.at(t).to_rgba8();
68 colored_img.put_pixel(x, y, image::Rgb([rgba[0], rgba[1], rgba[2]]));
69 }
70
71 if let Some(path) = viz_path {
73 colored_img.save(path)?;
74 }
75
76 norm_img.save(output_path)?;
78
79 Ok(DepthMapStats {
80 width,
81 height,
82 min_val,
83 max_val,
84 sample_depth_m: depth_in_meters,
85 })
86}