visual_odometry_rs/misc/
view.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5//! Helper functions to visualize images.
6
7use image::RgbImage;
8use nalgebra::DMatrix;
9
10use crate::core::inverse_depth::InverseDepth;
11use crate::misc::type_aliases::Float;
12use crate::misc::{colormap, interop};
13
14/// Creates an RGB image containing the gray image
15/// and candidates points overimposed in red.
16pub fn candidates_on_image(img: &DMatrix<u8>, candidates: &DMatrix<bool>) -> RgbImage {
17    let rgb_mat = img.zip_map(candidates, |i, a| fuse_img_with_color(i, (255, 0, 0), a));
18    interop::rgb_from_matrix(&rgb_mat)
19}
20
21fn fuse_img_with_color(intensity: u8, color: (u8, u8, u8), apply: bool) -> (u8, u8, u8) {
22    if apply {
23        color
24    } else {
25        (intensity, intensity, intensity)
26    }
27}
28
29/// Create an RGB image of an inverse depth map.
30/// Uses `idepth_enum_colormap` for the color choices.
31pub fn idepth_image(idepth_map: &DMatrix<InverseDepth>) -> RgbImage {
32    let viridis = &colormap::viridis_u8()[0..256];
33    let (d_min, d_max) = min_max(idepth_map).unwrap();
34    interop::rgb_from_matrix(
35        &idepth_map.map(|idepth| idepth_enum_colormap(viridis, d_min, d_max, &idepth)),
36    )
37}
38
39/// Find the minimum and maximum values in an inverse depth matrix.
40fn min_max(idepth_map: &DMatrix<InverseDepth>) -> Option<(Float, Float)> {
41    let mut min_temp: Option<Float> = None;
42    let mut max_temp: Option<Float> = None;
43    idepth_map.iter().for_each(|idepth| {
44        if let InverseDepth::WithVariance(id, _) = *idepth {
45            min_temp = min_temp.map(|x| x.min(id)).or_else(|| Some(id));
46            max_temp = max_temp.map(|x| x.max(id)).or_else(|| Some(id));
47        }
48    });
49    if let (Some(min_value), Some(max_value)) = (min_temp, max_temp) {
50        Some((min_value, max_value))
51    } else {
52        None
53    }
54}
55
56// INVERSE DEPTH HELPERS #############################################
57
58/// Visualize the enum as an 8-bits intensity:
59/// - `Unknown`:      black
60/// - `Discarded`:    gray
61/// - `WithVariance`: white
62pub fn idepth_enum(idepth: &InverseDepth) -> u8 {
63    match idepth {
64        InverseDepth::Unknown => 0_u8,
65        InverseDepth::Discarded => 50_u8,
66        InverseDepth::WithVariance(_, _) => 255_u8,
67    }
68}
69
70/// Visualize the enum with color depending on inverse depth:
71/// - `Unknown`:      black
72/// - `Discarded`:    red
73/// - `WithVariance`: viridis colormap
74#[allow(clippy::cast_sign_loss)]
75#[allow(clippy::cast_possible_truncation)]
76pub fn idepth_enum_colormap(
77    colormap: &[(u8, u8, u8)],
78    d_min: Float,
79    d_max: Float,
80    idepth: &InverseDepth,
81) -> (u8, u8, u8) {
82    match idepth {
83        InverseDepth::Unknown => (0, 0, 0),
84        InverseDepth::Discarded => (255, 0, 0),
85        InverseDepth::WithVariance(d, _) => {
86            let idx = (255.0 * (d - d_min) / (d_max - d_min)).round() as usize;
87            colormap[idx]
88        }
89    }
90}