use anyhow::Result;
use image::{Rgba, RgbaImage};
use crate::{draw_hbb, DrawContext, Drawable, Hbb, HbbStyle};
impl Drawable for [Hbb] {
fn draw(&self, ctx: &DrawContext, canvas: &mut RgbaImage) -> Result<()> {
self.iter().try_for_each(|x| x.draw(ctx, canvas))
}
}
impl Drawable for Hbb {
fn draw(&self, ctx: &DrawContext, canvas: &mut RgbaImage) -> Result<()> {
let default_style = HbbStyle::default();
let style = self.style().or(ctx.hbb_style).unwrap_or(&default_style);
if !style.visible() {
return Ok(());
}
let colors = ctx.resolve_hbb_colors(style, self.id());
if style.draw_fill() {
let mut overlay = RgbaImage::new(canvas.width(), canvas.height());
imageproc::drawing::draw_filled_rect_mut(
&mut overlay,
imageproc::rect::Rect::at(self.xmin().round() as i32, self.ymin().round() as i32)
.of_size(
(self.width().round() as u32).max(1),
(self.height().round() as u32).max(1),
),
Rgba(colors.fill.into()),
);
image::imageops::overlay(canvas, &overlay, 0, 0);
}
if style.draw_outline() {
let short_side = self.width().min(self.height());
let max_thickness = (short_side * style.thickness_max_ratio()).max(1.0) as usize;
let thickness = style.thickness().min(max_thickness).max(1);
let color = Rgba(colors.outline.into());
let mode = *style.mode();
let direction = *style.thickness_direction();
let x1 = self.xmin().round();
let y1 = self.ymin().round();
let x2 = self.xmax().round();
let y2 = self.ymax().round();
draw_hbb(canvas, x1, y1, x2, y2, color, mode, thickness, direction);
}
if let Some(keypoints) = self.keypoints() {
keypoints.to_vec().draw(ctx, canvas)?;
}
let text_style = style.text_style();
if style.text_visible() && text_style.should_draw() {
let label = self.meta().label(
text_style.id(),
text_style.name(),
text_style.confidence(),
text_style.decimal_places(),
);
let text_mode = *text_style.mode();
let text_thickness = text_style.thickness();
let font_size = text_style.font_size();
let box_size = ctx
.text_renderer
.box_size_with(&label, &text_mode, font_size)?;
let canvas_size = (canvas.width(), canvas.height());
let hbb_t = style.thickness() as f32;
let expansion = match style.thickness_direction() {
crate::ThicknessDirection::Outward => {
if hbb_t > 0.0 {
hbb_t - 1.0
} else {
0.0
}
}
crate::ThicknessDirection::Inward => 0.0,
crate::ThicknessDirection::Centered => {
if hbb_t > 1.0 {
(hbb_t - 1.0) / 2.0
} else {
0.0
}
}
};
let bbox = (
self.xmin() - expansion,
self.ymin() - expansion,
self.xmax() + expansion,
self.ymax() + expansion,
);
let text_offset = if matches!(
*text_style.loc(),
crate::TextLoc::OuterTopLeft
| crate::TextLoc::OuterTopCenter
| crate::TextLoc::OuterTopRight
) {
Some(text_thickness as f32)
} else {
None
};
let (x, y) =
text_style
.loc()
.compute_anchor(bbox, box_size, canvas_size, None, text_offset);
ctx.text_renderer.render_styled_with(
canvas,
&label,
x,
y,
colors.text,
colors.text_bg_fill,
colors.text_bg_outline,
text_mode,
text_style.draw_fill(),
text_style.draw_outline(),
text_thickness,
font_size,
)?;
}
Ok(())
}
}