Skip to content

Commit acd6f16

Browse files
committed
[*] refactor: image_composition support background-remove-mask
1 parent 376120a commit acd6f16

File tree

7 files changed

+193
-24
lines changed

7 files changed

+193
-24
lines changed

lib/background-remover/src/remover.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ impl BackgroundRemover {
244244
let mut mask = vec![0u8; (height * width) as usize];
245245
for i in 0..mask.len() {
246246
let clamped = data[i].max(0.0).min(1.0); // Clamp to [0, 1] and convert to 0-255
247-
mask[i] = (clamped * 255.0) as u8; //foreground (high) -> 255, background (low) -> 0
247+
mask[i] = (clamped * 255.0) as u8; // foreground (high) -> 255, background (low) -> 0
248248
}
249249

250250
let mask_image: GrayImage = ImageBuffer::from_raw(width, height, mask)
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use anyhow::Result;
2+
use camera::{
3+
ShapeCircle,
4+
image_composition::{MixPositionWithPadding, Shape, ShapeBase, mix_images_rgb},
5+
};
6+
use image::RgbImage;
7+
8+
fn main() -> Result<()> {
9+
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
10+
11+
log::info!("=== Background Removal Demo (RGB) ===");
12+
13+
log::info!("Loading camera image and mask...");
14+
let camera_image = image::open("./examples/test.png")
15+
.map_err(|e| anyhow::anyhow!("Failed to load camera image: {}", e))?
16+
.to_rgb8();
17+
18+
let mask = image::open("./examples/mask.png")
19+
.map_err(|e| anyhow::anyhow!("Failed to load mask: {}", e))?
20+
.to_luma8();
21+
22+
log::info!(
23+
" ✓ Loaded: {}x{}",
24+
camera_image.width(),
25+
camera_image.height()
26+
);
27+
28+
let background = RgbImage::from_fn(1920, 1080, |x, y| {
29+
let r = (x as f32 / 1920.0 * 180.0) as u8 + 40;
30+
let g = (y as f32 / 1080.0 * 180.0) as u8 + 40;
31+
let b = 150;
32+
image::Rgb([r, g, b])
33+
});
34+
35+
log::info!("1. Rectangle with background removal (no zoom)");
36+
let circle_no_zoom = ShapeCircle::default().with_radius(250).with_base(
37+
ShapeBase::default()
38+
.with_pos(MixPositionWithPadding::BottomRight((80, 80)))
39+
.with_zoom(1.0)
40+
.with_clip_pos((0.3, 0.2)),
41+
);
42+
43+
let result_no_zoom = mix_images_rgb(
44+
background.clone(),
45+
camera_image.clone(),
46+
Some(mask.clone()),
47+
Shape::Circle(circle_no_zoom),
48+
)?;
49+
result_no_zoom.save("tmp/bg_removal_no_zoom.png")?;
50+
log::info!(" ✓ Saved: tmp/bg_removal_no_zoom.png");
51+
52+
log::info!("2. Rectangle with background removal (1.5x zoom)");
53+
let circle_zoom = ShapeCircle::default().with_radius(250).with_base(
54+
ShapeBase::default()
55+
.with_pos(MixPositionWithPadding::BottomRight((80, 80)))
56+
.with_zoom(1.5)
57+
.with_clip_pos((0.3, 0.2)),
58+
);
59+
60+
let result_zoom = mix_images_rgb(
61+
background.clone(),
62+
camera_image.clone(),
63+
Some(mask.clone()),
64+
Shape::Circle(circle_zoom),
65+
)?;
66+
result_zoom.save("tmp/bg_removal_zoom.png")?;
67+
log::info!(" ✓ Saved: tmp/bg_removal_zoom.png");
68+
69+
log::info!("=== All tests completed! ===");
70+
log::info!("Generated files:");
71+
log::info!(" - tmp/bg_removal_no_zoom.png (No zoom, with mask)");
72+
log::info!(" - tmp/bg_removal_zoom.png (1.5x zoom, with mask)");
73+
74+
Ok(())
75+
}

lib/camera/examples/circle_demo.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ fn main() -> Result<()> {
3333
.with_clip_pos((0.5, 0.5)),
3434
);
3535

36-
let result1 = mix_images(bg1, test_image.clone(), Shape::Circle(circle1))?;
36+
let result1 = mix_images(bg1, test_image.clone(), None, Shape::Circle(circle1))?;
3737
result1.save("tmp/circle_zoom_1.0x.png")?;
3838
log::info!(" ✓ Saved circle_zoom_1.0x.png");
3939

@@ -47,7 +47,7 @@ fn main() -> Result<()> {
4747
.with_clip_pos((0.2, 0.3)),
4848
);
4949

50-
let result2 = mix_images(bg2, test_image.clone(), Shape::Circle(circle2))?;
50+
let result2 = mix_images(bg2, test_image.clone(), None, Shape::Circle(circle2))?;
5151
result2.save("tmp/circle_zoom_2.0x.png")?;
5252
log::info!(" ✓ Saved circle_zoom_2.0x.png");
5353

@@ -56,7 +56,7 @@ fn main() -> Result<()> {
5656
let circle3 = ShapeCircle::default()
5757
.with_radius(100)
5858
.with_base(ShapeBase::default().with_border_width(3).with_zoom(0.5));
59-
let result3 = mix_images(bg3, test_image.clone(), Shape::Circle(circle3))?;
59+
let result3 = mix_images(bg3, test_image.clone(), None, Shape::Circle(circle3))?;
6060
result3.save("tmp/circle_zoom_0.5x.png")?;
6161
log::info!(" ✓ Saved circle_zoom_0.5x.png");
6262

lib/camera/examples/image_composition_demo.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
3737
let result_rect = mix_images(
3838
background_rgba.clone(),
3939
camera_image_rgba.clone(),
40+
None,
4041
Shape::Rectangle(rect),
4142
)?;
4243
result_rect.save("tmp/composition_rectangle_rgba.png")?;
@@ -52,6 +53,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
5253
let result_circle = mix_images(
5354
background_rgba.clone(),
5455
camera_image_rgba.clone(),
56+
None,
5557
Shape::Circle(circle),
5658
)?;
5759
result_circle.save("tmp/composition_circle_rgba.png")?;
@@ -85,6 +87,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
8587
let result_rect_rgb = mix_images_rgb(
8688
background_rgb.clone(),
8789
camera_image_rgb.clone(),
90+
None,
8891
Shape::Rectangle(rect_rgb),
8992
)?;
9093
result_rect_rgb.save("tmp/composition_rectangle_rgb.png")?;
@@ -100,6 +103,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
100103
let result_circle_rgb = mix_images_rgb(
101104
background_rgb.clone(),
102105
camera_image_rgb.clone(),
106+
None,
103107
Shape::Circle(circle_rgb),
104108
)?;
105109
result_circle_rgb.save("tmp/composition_circle_rgb.png")?;
@@ -115,6 +119,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
115119
let result_plain_rgb = mix_images_rgb(
116120
background_rgb.clone(),
117121
camera_image_rgb.clone(),
122+
None,
118123
Shape::Rectangle(plain_rect_rgb),
119124
)?;
120125
result_plain_rgb.save("tmp/composition_rect_plain_rgb.png")?;
@@ -130,6 +135,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
130135
let result_plain_rgb = mix_images_rgb(
131136
background_rgb.clone(),
132137
camera_image_rgb.clone(),
138+
None,
133139
Shape::Circle(plain_circle_rgb),
134140
)?;
135141
result_plain_rgb.save("tmp/composition_circle_plain_rgb.png")?;
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ fn main() -> Result<()> {
2727
.with_border_color(Rgba([255, 0, 0, 255]))
2828
.with_clip_pos((0.1, 0.1)),
2929
);
30-
let result1 = mix_images(bg1, test_image.clone(), Shape::Rectangle(rect1))?;
30+
let result1 = mix_images(bg1, test_image.clone(), None, Shape::Rectangle(rect1))?;
3131
result1.save("tmp/rect_zoom_1.0x.png")?;
3232
log::info!(" ✓ Saved zoom_1.0x.png (original size)");
3333

@@ -41,7 +41,7 @@ fn main() -> Result<()> {
4141
.with_border_color(Rgba([255, 0, 0, 255]))
4242
.with_clip_pos((0.25, 0.25)),
4343
);
44-
let result2 = mix_images(bg2, test_image.clone(), Shape::Rectangle(rect2))?;
44+
let result2 = mix_images(bg2, test_image.clone(), None, Shape::Rectangle(rect2))?;
4545
result2.save("tmp/rect_zoom_2.0x.png")?;
4646
log::info!(" ✓ Saved zoom_2.0x.png (center cropped, 2x magnified)");
4747

@@ -54,7 +54,7 @@ fn main() -> Result<()> {
5454
.with_border_color(Rgba([255, 0, 0, 255]))
5555
.with_clip_pos((0.30, 0.30)),
5656
);
57-
let result3 = mix_images(bg3, test_image.clone(), Shape::Rectangle(rect3))?;
57+
let result3 = mix_images(bg3, test_image.clone(), None, Shape::Rectangle(rect3))?;
5858
result3.save("tmp/rect_zoom_0.5x.png")?;
5959
log::info!(" ✓ Saved zoom_0.5x.png (shrunk with black padding)");
6060

0 commit comments

Comments
 (0)