Skip to content

Commit

Permalink
Remove most usages of dynamic_image
Browse files Browse the repository at this point in the history
  • Loading branch information
jleibs committed Mar 11, 2023
1 parent 007303f commit 2aed7c1
Showing 1 changed file with 163 additions and 167 deletions.
330 changes: 163 additions & 167 deletions crates/re_viewer/src/ui/data_ui/image.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use egui::Vec2;
use egui::{ColorImage, Vec2};
use itertools::Itertools as _;

use re_log_types::component_types::{ClassId, Tensor, TensorDataMeaning, TensorTrait};
Expand Down Expand Up @@ -99,7 +99,7 @@ impl DataUi for Tensor {
ui.horizontal(|ui| image_options(ui, self, &dynamic_img));

// TODO(emilk): support histograms of non-RGB images too
if let image::DynamicImage::ImageRgb8(rgb_image) = dynamic_img {
if let image::DynamicImage::ImageRgba8(rgb_image) = dynamic_img {
ui.collapsing("Histogram", |ui| {
histogram_ui(ui, &rgb_image);
});
Expand Down Expand Up @@ -181,20 +181,26 @@ fn show_zoomed_image_region_tooltip(
.on_hover_ui_at_pointer(|ui| {
ui.set_max_width(320.0);
ui.horizontal(|ui| {
let Some(dynamic_img) = tensor_view.dynamic_img() else { return };
let w = dynamic_img.width() as _;
let h = dynamic_img.height() as _;

use egui::NumExt;

let center = [
(egui::remap(pointer_pos.x, image_rect.x_range(), 0.0..=w as f32) as isize)
.at_most(w),
(egui::remap(pointer_pos.y, image_rect.y_range(), 0.0..=h as f32) as isize)
.at_most(h),
];
show_zoomed_image_region_area_outline(parent_ui, tensor_view, center, image_rect);
show_zoomed_image_region(ui, tensor_view, center, meter);
if tensor_view.tensor.is_shaped_like_an_image() {
let h = tensor_view.tensor.shape()[0].size as _;
let w = tensor_view.tensor.shape()[1].size as _;

use egui::NumExt;

let center = [
(egui::remap(pointer_pos.x, image_rect.x_range(), 0.0..=w as f32) as isize)
.at_most(w),
(egui::remap(pointer_pos.y, image_rect.y_range(), 0.0..=h as f32) as isize)
.at_most(h),
];
show_zoomed_image_region_area_outline(
parent_ui,
tensor_view,
center,
image_rect,
);
show_zoomed_image_region(ui, tensor_view, center, meter);
}
});
})
}
Expand All @@ -208,29 +214,29 @@ pub fn show_zoomed_image_region_area_outline(
[center_x, center_y]: [isize; 2],
image_rect: egui::Rect,
) {
let Some(dynamic_img) = tensor_view.dynamic_img() else { return };

use egui::{pos2, remap, Color32, Rect};

let w = dynamic_img.width() as _;
let h = dynamic_img.height() as _;

// Show where on the original image the zoomed-in region is at:
let left = (center_x - ZOOMED_IMAGE_TEXEL_RADIUS) as f32;
let right = (center_x + ZOOMED_IMAGE_TEXEL_RADIUS) as f32;
let top = (center_y - ZOOMED_IMAGE_TEXEL_RADIUS) as f32;
let bottom = (center_y + ZOOMED_IMAGE_TEXEL_RADIUS) as f32;

let left = remap(left, 0.0..=w, image_rect.x_range());
let right = remap(right, 0.0..=w, image_rect.x_range());
let top = remap(top, 0.0..=h, image_rect.y_range());
let bottom = remap(bottom, 0.0..=h, image_rect.y_range());

let rect = Rect::from_min_max(pos2(left, top), pos2(right, bottom));
// TODO(emilk): use `parent_ui.painter()` and put it in a high Z layer, when https://github.com/emilk/egui/issues/1516 is done
let painter = ui.ctx().debug_painter();
painter.rect_stroke(rect, 0.0, (2.0, Color32::BLACK));
painter.rect_stroke(rect, 0.0, (1.0, Color32::WHITE));
if tensor_view.tensor.is_shaped_like_an_image() {
use egui::{pos2, remap, Color32, Rect};

let h = tensor_view.tensor.shape()[0].size as _;
let w = tensor_view.tensor.shape()[1].size as _;

// Show where on the original image the zoomed-in region is at:
let left = (center_x - ZOOMED_IMAGE_TEXEL_RADIUS) as f32;
let right = (center_x + ZOOMED_IMAGE_TEXEL_RADIUS) as f32;
let top = (center_y - ZOOMED_IMAGE_TEXEL_RADIUS) as f32;
let bottom = (center_y + ZOOMED_IMAGE_TEXEL_RADIUS) as f32;

let left = remap(left, 0.0..=w, image_rect.x_range());
let right = remap(right, 0.0..=w, image_rect.x_range());
let top = remap(top, 0.0..=h, image_rect.y_range());
let bottom = remap(bottom, 0.0..=h, image_rect.y_range());

let rect = Rect::from_min_max(pos2(left, top), pos2(right, bottom));
// TODO(emilk): use `parent_ui.painter()` and put it in a high Z layer, when https://github.com/emilk/egui/issues/1516 is done
let painter = ui.ctx().debug_painter();
painter.rect_stroke(rect, 0.0, (2.0, Color32::BLACK));
painter.rect_stroke(rect, 0.0, (1.0, Color32::WHITE));
}
}

/// `meter`: iff this is a depth map, how long is one meter?
Expand All @@ -240,159 +246,149 @@ pub fn show_zoomed_image_region(
image_position: [isize; 2],
meter: Option<f32>,
) {
let Some(dynamic_img) = tensor_view.dynamic_img() else { return };

use egui::{color_picker, pos2, remap, Color32, Mesh, Rect};

const POINTS_PER_TEXEL: f32 = 5.0;
let size = Vec2::splat((ZOOMED_IMAGE_TEXEL_RADIUS * 2 + 1) as f32 * POINTS_PER_TEXEL);

let (_id, zoom_rect) = tooltip_ui.allocate_space(size);
let painter = tooltip_ui.painter();

painter.rect_filled(zoom_rect, 0.0, tooltip_ui.visuals().extreme_bg_color);

let mut mesh = Mesh::default();
let mut center_texel_rect = None;
for dx in -ZOOMED_IMAGE_TEXEL_RADIUS..=ZOOMED_IMAGE_TEXEL_RADIUS {
for dy in -ZOOMED_IMAGE_TEXEL_RADIUS..=ZOOMED_IMAGE_TEXEL_RADIUS {
let x = image_position[0] + dx;
let y = image_position[1] + dy;
let color = get_pixel(&dynamic_img, [x, y]);
if let Some(color) = color {
let image::Rgba([r, g, b, a]) = color;
let color = egui::Color32::from_rgba_unmultiplied(r, g, b, a);

if color != Color32::TRANSPARENT {
let tr = ZOOMED_IMAGE_TEXEL_RADIUS as f32;
let left = remap(dx as f32, -tr..=(tr + 1.0), zoom_rect.x_range());
let right = remap((dx + 1) as f32, -tr..=(tr + 1.0), zoom_rect.x_range());
let top = remap(dy as f32, -tr..=(tr + 1.0), zoom_rect.y_range());
let bottom = remap((dy + 1) as f32, -tr..=(tr + 1.0), zoom_rect.y_range());
let rect = Rect {
min: pos2(left, top),
max: pos2(right, bottom),
};
mesh.add_colored_rect(rect, color);

if dx == 0 && dy == 0 {
center_texel_rect = Some(rect);
if let Some(colored_image) = tensor_view.colored_image {
use egui::{color_picker, pos2, remap, Color32, Mesh, Rect};

const POINTS_PER_TEXEL: f32 = 5.0;
let size = Vec2::splat((ZOOMED_IMAGE_TEXEL_RADIUS * 2 + 1) as f32 * POINTS_PER_TEXEL);

let (_id, zoom_rect) = tooltip_ui.allocate_space(size);
let painter = tooltip_ui.painter();

painter.rect_filled(zoom_rect, 0.0, tooltip_ui.visuals().extreme_bg_color);

let mut mesh = Mesh::default();
let mut center_texel_rect = None;
for dx in -ZOOMED_IMAGE_TEXEL_RADIUS..=ZOOMED_IMAGE_TEXEL_RADIUS {
for dy in -ZOOMED_IMAGE_TEXEL_RADIUS..=ZOOMED_IMAGE_TEXEL_RADIUS {
let x = image_position[0] + dx;
let y = image_position[1] + dy;
let color = get_pixel(colored_image, [x, y]);
if let Some(color) = color {
if color != Color32::TRANSPARENT {
let tr = ZOOMED_IMAGE_TEXEL_RADIUS as f32;
let left = remap(dx as f32, -tr..=(tr + 1.0), zoom_rect.x_range());
let right = remap((dx + 1) as f32, -tr..=(tr + 1.0), zoom_rect.x_range());
let top = remap(dy as f32, -tr..=(tr + 1.0), zoom_rect.y_range());
let bottom = remap((dy + 1) as f32, -tr..=(tr + 1.0), zoom_rect.y_range());
let rect = Rect {
min: pos2(left, top),
max: pos2(right, bottom),
};
mesh.add_colored_rect(rect, color);

if dx == 0 && dy == 0 {
center_texel_rect = Some(rect);
}
}
}
}
}
}

painter.add(mesh);
painter.add(mesh);

if let Some(center_texel_rect) = center_texel_rect {
painter.rect_stroke(center_texel_rect, 0.0, (2.0, Color32::BLACK));
painter.rect_stroke(center_texel_rect, 0.0, (1.0, Color32::WHITE));
}
if let Some(center_texel_rect) = center_texel_rect {
painter.rect_stroke(center_texel_rect, 0.0, (2.0, Color32::BLACK));
painter.rect_stroke(center_texel_rect, 0.0, (1.0, Color32::WHITE));
}

if let Some(color) = get_pixel(&dynamic_img, image_position) {
tooltip_ui.separator();
let (x, y) = (image_position[0] as _, image_position[1] as _);

tooltip_ui.vertical(|ui| {
egui::Grid::new("hovered pixel properties").show(ui, |ui| {
ui.label("Position:");
ui.label(format!("{}, {}", image_position[0], image_position[1]));
ui.end_row();

if tensor_view.tensor.num_dim() == 2 {
if let Some(raw_value) = tensor_view.tensor.get(&[y, x]) {
if let (TensorDataMeaning::ClassId, annotations, Some(u16_val)) = (
tensor_view.tensor.meaning(),
tensor_view.annotations,
raw_value.try_as_u16(),
) {
ui.label("Label:");
ui.label(
annotations
.class_description(Some(ClassId(u16_val)))
.annotation_info()
.label(None)
.unwrap_or_default(),
);
ui.end_row();
};
if let Some(color) = get_pixel(colored_image, image_position) {
tooltip_ui.separator();
let (x, y) = (image_position[0] as _, image_position[1] as _);

tooltip_ui.vertical(|ui| {
egui::Grid::new("hovered pixel properties").show(ui, |ui| {
ui.label("Position:");
ui.label(format!("{}, {}", image_position[0], image_position[1]));
ui.end_row();

if tensor_view.tensor.num_dim() == 2 {
if let Some(raw_value) = tensor_view.tensor.get(&[y, x]) {
if let (TensorDataMeaning::ClassId, annotations, Some(u16_val)) = (
tensor_view.tensor.meaning(),
tensor_view.annotations,
raw_value.try_as_u16(),
) {
ui.label("Label:");
ui.label(
annotations
.class_description(Some(ClassId(u16_val)))
.annotation_info()
.label(None)
.unwrap_or_default(),
);
ui.end_row();
};
}
}
}
if let Some(meter) = meter {
// This is a depth map
if let Some(raw_value) = tensor_view.tensor.get(&[y, x]) {
let raw_value = raw_value.as_f64();
let meters = raw_value / meter as f64;
ui.label("Depth:");
if meters < 1.0 {
ui.monospace(format!("{:.1} mm", meters * 1e3));
} else {
ui.monospace(format!("{meters:.3} m"));
if let Some(meter) = meter {
// This is a depth map
if let Some(raw_value) = tensor_view.tensor.get(&[y, x]) {
let raw_value = raw_value.as_f64();
let meters = raw_value / meter as f64;
ui.label("Depth:");
if meters < 1.0 {
ui.monospace(format!("{:.1} mm", meters * 1e3));
} else {
ui.monospace(format!("{meters:.3} m"));
}
}
}
}
});

use image::DynamicImage;

let image::Rgba([r, g, b, a]) = color;
});

let text = match dynamic_img {
DynamicImage::ImageLuma8(_) => {
format!("L: {r}")
}
/*
let text = match dynamic_img {
DynamicImage::ImageLuma8(_) => {
format!("L: {r}")
}
DynamicImage::ImageLuma16(image) => {
let l = image.get_pixel(x as _, y as _)[0];
format!("L: {} ({:.5})", l, l as f32 / 65535.0)
}
DynamicImage::ImageLuma16(image) => {
let l = image.get_pixel(x as _, y as _)[0];
format!("L: {} ({:.5})", l, l as f32 / 65535.0)
}
DynamicImage::ImageLumaA8(_) | DynamicImage::ImageLumaA16(_) => {
format!("L: {r}, A: {a}")
}
DynamicImage::ImageLumaA8(_) | DynamicImage::ImageLumaA16(_) => {
format!("L: {r}, A: {a}")
}
DynamicImage::ImageRgb8(_)
| DynamicImage::ImageRgb16(_)
| DynamicImage::ImageRgb32F(_) => {
// TODO(emilk): show 16-bit and 32f values differently
format!("R: {r}, G: {g}, B: {b}, #{r:02X}{g:02X}{b:02X}")
}
DynamicImage::ImageRgb8(_)
| DynamicImage::ImageRgb16(_)
| DynamicImage::ImageRgb32F(_) => {
// TODO(emilk): show 16-bit and 32f values differently
format!("R: {r}, G: {g}, B: {b}, #{r:02X}{g:02X}{b:02X}")
}
DynamicImage::ImageRgba8(_)
| DynamicImage::ImageRgba16(_)
| DynamicImage::ImageRgba32F(_) => {
// TODO(emilk): show 16-bit and 32f values differently
format!("R: {r}, G: {g}, B: {b}, A: {a}, #{r:02X}{g:02X}{b:02X}{a:02X}")
}
DynamicImage::ImageRgba8(_)
| DynamicImage::ImageRgba16(_)
| DynamicImage::ImageRgba32F(_) => {
// TODO(emilk): show 16-bit and 32f values differently
format!("R: {r}, G: {g}, B: {b}, A: {a}, #{r:02X}{g:02X}{b:02X}{a:02X}")
}
_ => {
re_log::warn_once!("Unknown image color type: {:?}", dynamic_img.color());
format!("R: {r}, G: {g}, B: {b}, A: {a}, #{r:02X}{g:02X}{b:02X}{a:02X}")
}
};
ui.label(text);
_ => {
re_log::warn_once!("Unknown image color type: {:?}", dynamic_img.color());
format!("R: {r}, G: {g}, B: {b}, A: {a}, #{r:02X}{g:02X}{b:02X}{a:02X}")
}
};
ui.label(text);
*/
ui.label("TODO(jleibs): Hover-value");

color_picker::show_color(
ui,
Color32::from_rgba_unmultiplied(r, g, b, a),
Vec2::splat(ui.available_height()),
);
});
color_picker::show_color(ui, color, Vec2::splat(ui.available_height()));
});
}
}
}

fn get_pixel(image: &image::DynamicImage, [x, y]: [isize; 2]) -> Option<image::Rgba<u8>> {
use image::GenericImageView;

if x < 0 || y < 0 || image.width() <= x as u32 || image.height() <= y as u32 {
fn get_pixel(image: &ColorImage, [x, y]: [isize; 2]) -> Option<egui::Color32> {
if x < 0 || y < 0 || image.width() as isize <= x || image.height() as isize <= y {
None
} else {
Some(image.get_pixel(x as u32, y as u32))
Some(image[(x as _, y as _)])
}
}

fn histogram_ui(ui: &mut egui::Ui, rgb_image: &image::RgbImage) -> egui::Response {
fn histogram_ui(ui: &mut egui::Ui, rgb_image: &image::RgbaImage) -> egui::Response {
crate::profile_function!();

let mut histograms = [[0_u64; 256]; 3];
Expand Down

0 comments on commit 2aed7c1

Please sign in to comment.