Use imageproc erode
This commit is contained in:
56
Cargo.lock
generated
56
Cargo.lock
generated
@@ -139,7 +139,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cc836ad7a40dc9d8049574e2a29979f5dc77deeea4d7ebcd29773452f0e9694"
|
||||
dependencies = [
|
||||
"approx 0.3.2",
|
||||
"libm",
|
||||
"libm 0.1.4",
|
||||
"num-complex 0.2.4",
|
||||
"num-traits",
|
||||
]
|
||||
@@ -2044,8 +2044,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2399,6 +2401,24 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imageproc"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2393fb7808960751a52e8a154f67e7dd3f8a2ef9bd80d1553078a7b4e8ed3f0d"
|
||||
dependencies = [
|
||||
"ab_glyph",
|
||||
"approx 0.5.1",
|
||||
"getrandom",
|
||||
"image 0.25.1",
|
||||
"itertools 0.12.1",
|
||||
"nalgebra 0.32.6",
|
||||
"num 0.4.3",
|
||||
"rand 0.8.5",
|
||||
"rand_distr",
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imgref"
|
||||
version = "1.10.1"
|
||||
@@ -2633,6 +2653,12 @@ version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.0.2"
|
||||
@@ -2844,6 +2870,7 @@ dependencies = [
|
||||
"encase",
|
||||
"goo_format",
|
||||
"image 0.25.1",
|
||||
"imageproc",
|
||||
"nalgebra 0.32.6",
|
||||
"notify-rust",
|
||||
"open",
|
||||
@@ -3052,6 +3079,20 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
|
||||
dependencies = [
|
||||
"num-bigint 0.4.5",
|
||||
"num-complex 0.4.6",
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-rational 0.4.2",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.2.6"
|
||||
@@ -3159,6 +3200,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg 1.3.0",
|
||||
"libm 0.2.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3544,7 +3586,7 @@ dependencies = [
|
||||
"fool",
|
||||
"itertools 0.8.2",
|
||||
"nalgebra 0.17.3",
|
||||
"num",
|
||||
"num 0.2.1",
|
||||
"smallvec 0.6.14",
|
||||
"typenum",
|
||||
]
|
||||
@@ -3841,6 +3883,16 @@ dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_distr"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"rand 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.1.0"
|
||||
|
@@ -21,6 +21,7 @@ egui-phosphor = { git = "https://github.com/connorslade/egui-phosphor" }
|
||||
egui-wgpu = "0.27.2"
|
||||
encase = { version = "0.8.0", features = ["nalgebra"] }
|
||||
image = "0.25.1"
|
||||
imageproc = "0.25.0"
|
||||
md5 = "0.7.0"
|
||||
nalgebra = "0.32.6"
|
||||
notify-rust = "4.11.0"
|
||||
@@ -40,4 +41,4 @@ stl_io = "0.7.0"
|
||||
toml = "0.8.16"
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = "0.3.18"
|
||||
wgpu = "0.19.4"
|
||||
wgpu = "0.19.4"
|
||||
|
@@ -33,6 +33,14 @@ impl Image {
|
||||
image
|
||||
}
|
||||
|
||||
pub fn from_raw(width: usize, height: usize, data: Vec<u8>) -> Self {
|
||||
Self {
|
||||
size: Vector2::new(width, height),
|
||||
data,
|
||||
idx: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_run(&mut self, length: usize, value: u8) {
|
||||
self.data[self.idx..self.idx + length].fill(value);
|
||||
self.idx += length;
|
||||
|
@@ -3,3 +3,4 @@ pub mod image;
|
||||
pub mod misc;
|
||||
pub mod oklab;
|
||||
pub mod serde;
|
||||
pub mod rle_image;
|
||||
|
88
common/src/rle_image.rs
Normal file
88
common/src/rle_image.rs
Normal file
@@ -0,0 +1,88 @@
|
||||
use crate::misc::Run;
|
||||
|
||||
pub struct RleImage {
|
||||
runs: Vec<ImageRun>,
|
||||
width: usize,
|
||||
height: usize,
|
||||
}
|
||||
|
||||
pub struct RleImageIterator<'a> {
|
||||
image: &'a RleImage,
|
||||
run_index: usize,
|
||||
last_idx: usize,
|
||||
}
|
||||
|
||||
pub struct ImageRun {
|
||||
color: u8,
|
||||
|
||||
row: usize,
|
||||
start: usize,
|
||||
end: usize,
|
||||
}
|
||||
|
||||
impl RleImage {
|
||||
pub fn from_decoder(width: usize, height: usize, decoder: impl Iterator<Item = Run>) -> Self {
|
||||
let mut runs = Vec::new();
|
||||
|
||||
let mut pixel = 0;
|
||||
for run in decoder {
|
||||
let length = run.length as usize;
|
||||
if run.value != 0 {
|
||||
let (y, x) = (pixel / width, pixel % width);
|
||||
runs.push(ImageRun {
|
||||
color: run.value,
|
||||
row: y,
|
||||
start: x,
|
||||
end: x + length,
|
||||
});
|
||||
}
|
||||
pixel += length;
|
||||
}
|
||||
|
||||
Self {
|
||||
runs,
|
||||
width,
|
||||
height,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_runs(&self) -> RleImageIterator {
|
||||
RleImageIterator {
|
||||
image: self,
|
||||
run_index: 0,
|
||||
last_idx: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for RleImageIterator<'a> {
|
||||
type Item = Run;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.run_index >= self.image.runs.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let run = &self.image.runs[self.run_index];
|
||||
let row_offset = run.row * self.image.width;
|
||||
let start_idx = row_offset + run.start;
|
||||
|
||||
if self.last_idx < start_idx {
|
||||
let length = start_idx - self.last_idx;
|
||||
self.last_idx = start_idx;
|
||||
return Some(Run {
|
||||
length: length as u64,
|
||||
value: 0,
|
||||
});
|
||||
}
|
||||
|
||||
let length = run.end - run.start;
|
||||
self.last_idx = row_offset + run.end;
|
||||
self.run_index += 1;
|
||||
|
||||
Some(Run {
|
||||
length: length as u64,
|
||||
value: run.color,
|
||||
})
|
||||
}
|
||||
}
|
@@ -18,6 +18,7 @@ egui-wgpu.workspace = true
|
||||
egui.workspace = true
|
||||
encase.workspace = true
|
||||
image.workspace = true
|
||||
imageproc.workspace = true
|
||||
nalgebra.workspace = true
|
||||
notify-rust.workspace = true
|
||||
open.workspace = true
|
||||
|
@@ -1,7 +1,14 @@
|
||||
use common::image::Image;
|
||||
use std::{
|
||||
collections::{HashSet, VecDeque},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use common::{image::Image, rle_image::RleImage};
|
||||
use egui::{Context, Ui};
|
||||
use image::{GrayImage, Luma};
|
||||
use imageproc::{distance_transform::Norm, morphology::Mask};
|
||||
use rayon::iter::{ParallelBridge, ParallelIterator};
|
||||
use tracing::info;
|
||||
use tracing::{info, trace};
|
||||
|
||||
use crate::{app::App, ui::components::dragger_tip};
|
||||
use goo_format::{File as GooFile, LayerDecoder, LayerEncoder};
|
||||
@@ -60,19 +67,29 @@ impl Plugin for ElephantFootFixerPlugin {
|
||||
x_radius, y_radius
|
||||
);
|
||||
|
||||
let intensity = self.intensity_multiplier / 100.0;
|
||||
let darken = |value: u8| (value as f32 * intensity).round() as u8;
|
||||
|
||||
goo.layers
|
||||
.iter_mut()
|
||||
.take(goo.header.bottom_layers as usize)
|
||||
.par_bridge()
|
||||
.for_each(|layer| {
|
||||
let decoder = LayerDecoder::new(&layer.data);
|
||||
let mut image = Image::from_decoder(width, height, decoder);
|
||||
let raw_image = Image::from_decoder(width, height, decoder).take();
|
||||
let mut image =
|
||||
GrayImage::from_raw(width as u32, height as u32, raw_image).unwrap();
|
||||
|
||||
let intensity = self.intensity_multiplier / 100.0;
|
||||
apply(&mut image, intensity, x_radius, y_radius);
|
||||
let erode = imageproc::morphology::grayscale_erode(&image, &Mask::square(10));
|
||||
for (x, y, pixel) in image.enumerate_pixels_mut() {
|
||||
if erode.get_pixel(x, y)[0] == 0 && pixel[0] != 0 {
|
||||
*pixel = Luma([darken(pixel[0])]);
|
||||
}
|
||||
}
|
||||
|
||||
let mut new_layer = LayerEncoder::new();
|
||||
for run in image.runs() {
|
||||
let raw_image = Image::from_raw(width, height, image.into_raw());
|
||||
for run in raw_image.runs() {
|
||||
new_layer.add_run(run.length, run.value)
|
||||
}
|
||||
|
||||
@@ -90,67 +107,3 @@ pub fn get_plugin() -> Box<dyn Plugin> {
|
||||
intensity_multiplier: 30.0,
|
||||
})
|
||||
}
|
||||
|
||||
fn apply(image: &mut Image, intensity: f32, x_radius: usize, y_radius: usize) {
|
||||
let mut x_distances = vec![u16::MAX; image.size.x * image.size.y];
|
||||
let mut y_distances = vec![u16::MAX; image.size.x * image.size.y];
|
||||
|
||||
#[inline(always)]
|
||||
fn update_distance(
|
||||
image: &Image,
|
||||
distances: &mut [u16],
|
||||
distance: &mut u16,
|
||||
x: usize,
|
||||
y: usize,
|
||||
) {
|
||||
*distance += 1;
|
||||
|
||||
let pixel = image.get_pixel(x, y);
|
||||
(pixel == 0).then(|| *distance = 0);
|
||||
|
||||
let idx = y * image.size.x + x;
|
||||
let old = distances[idx];
|
||||
(*distance < old).then(|| distances[idx] = *distance);
|
||||
}
|
||||
|
||||
for x in 0..image.size.x {
|
||||
let mut distance = 0;
|
||||
for y in 0..image.size.y {
|
||||
update_distance(&image, &mut y_distances, &mut distance, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
for y in 0..image.size.y {
|
||||
let mut distance = 0;
|
||||
for x in 0..image.size.x {
|
||||
update_distance(&image, &mut x_distances, &mut distance, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
for x in (0..image.size.x).rev() {
|
||||
let mut distance = 0;
|
||||
for y in (0..image.size.y).rev() {
|
||||
update_distance(&image, &mut y_distances, &mut distance, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
for y in (0..image.size.y).rev() {
|
||||
let mut distance = 0;
|
||||
for x in (0..image.size.x).rev() {
|
||||
update_distance(&image, &mut x_distances, &mut distance, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
for x in 0..image.size.x {
|
||||
for y in 0..image.size.y {
|
||||
let pixel = image.get_pixel(x, y);
|
||||
|
||||
let x_distance = x_distances[y * image.size.x + x];
|
||||
let y_distance = y_distances[y * image.size.x + x];
|
||||
|
||||
if x_distance < x_radius as u16 || y_distance < y_radius as u16 {
|
||||
image.set_pixel(x, y, (pixel as f32 * intensity).round() as u8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -33,6 +33,7 @@ fn main() -> Result<()> {
|
||||
..Default::default()
|
||||
},
|
||||
first_layers: 10,
|
||||
transition_layers: 10,
|
||||
};
|
||||
|
||||
let file = File::open(FILE_PATH)?;
|
||||
|
Reference in New Issue
Block a user