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"
|
checksum = "2cc836ad7a40dc9d8049574e2a29979f5dc77deeea4d7ebcd29773452f0e9694"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"approx 0.3.2",
|
"approx 0.3.2",
|
||||||
"libm",
|
"libm 0.1.4",
|
||||||
"num-complex 0.2.4",
|
"num-complex 0.2.4",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
@@ -2044,8 +2044,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2399,6 +2401,24 @@ dependencies = [
|
|||||||
"thiserror",
|
"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]]
|
[[package]]
|
||||||
name = "imgref"
|
name = "imgref"
|
||||||
version = "1.10.1"
|
version = "1.10.1"
|
||||||
@@ -2633,6 +2653,12 @@ version = "0.1.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a"
|
checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libm"
|
||||||
|
version = "0.2.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libredox"
|
name = "libredox"
|
||||||
version = "0.0.2"
|
version = "0.0.2"
|
||||||
@@ -2844,6 +2870,7 @@ dependencies = [
|
|||||||
"encase",
|
"encase",
|
||||||
"goo_format",
|
"goo_format",
|
||||||
"image 0.25.1",
|
"image 0.25.1",
|
||||||
|
"imageproc",
|
||||||
"nalgebra 0.32.6",
|
"nalgebra 0.32.6",
|
||||||
"notify-rust",
|
"notify-rust",
|
||||||
"open",
|
"open",
|
||||||
@@ -3052,6 +3079,20 @@ dependencies = [
|
|||||||
"num-traits",
|
"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]]
|
[[package]]
|
||||||
name = "num-bigint"
|
name = "num-bigint"
|
||||||
version = "0.2.6"
|
version = "0.2.6"
|
||||||
@@ -3159,6 +3200,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg 1.3.0",
|
"autocfg 1.3.0",
|
||||||
|
"libm 0.2.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3544,7 +3586,7 @@ dependencies = [
|
|||||||
"fool",
|
"fool",
|
||||||
"itertools 0.8.2",
|
"itertools 0.8.2",
|
||||||
"nalgebra 0.17.3",
|
"nalgebra 0.17.3",
|
||||||
"num",
|
"num 0.2.1",
|
||||||
"smallvec 0.6.14",
|
"smallvec 0.6.14",
|
||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
@@ -3841,6 +3883,16 @@ dependencies = [
|
|||||||
"getrandom",
|
"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]]
|
[[package]]
|
||||||
name = "rand_hc"
|
name = "rand_hc"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@@ -21,6 +21,7 @@ egui-phosphor = { git = "https://github.com/connorslade/egui-phosphor" }
|
|||||||
egui-wgpu = "0.27.2"
|
egui-wgpu = "0.27.2"
|
||||||
encase = { version = "0.8.0", features = ["nalgebra"] }
|
encase = { version = "0.8.0", features = ["nalgebra"] }
|
||||||
image = "0.25.1"
|
image = "0.25.1"
|
||||||
|
imageproc = "0.25.0"
|
||||||
md5 = "0.7.0"
|
md5 = "0.7.0"
|
||||||
nalgebra = "0.32.6"
|
nalgebra = "0.32.6"
|
||||||
notify-rust = "4.11.0"
|
notify-rust = "4.11.0"
|
||||||
@@ -40,4 +41,4 @@ stl_io = "0.7.0"
|
|||||||
toml = "0.8.16"
|
toml = "0.8.16"
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.40"
|
||||||
tracing-subscriber = "0.3.18"
|
tracing-subscriber = "0.3.18"
|
||||||
wgpu = "0.19.4"
|
wgpu = "0.19.4"
|
||||||
|
@@ -33,6 +33,14 @@ impl Image {
|
|||||||
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) {
|
pub fn add_run(&mut self, length: usize, value: u8) {
|
||||||
self.data[self.idx..self.idx + length].fill(value);
|
self.data[self.idx..self.idx + length].fill(value);
|
||||||
self.idx += length;
|
self.idx += length;
|
||||||
|
@@ -3,3 +3,4 @@ pub mod image;
|
|||||||
pub mod misc;
|
pub mod misc;
|
||||||
pub mod oklab;
|
pub mod oklab;
|
||||||
pub mod serde;
|
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
|
egui.workspace = true
|
||||||
encase.workspace = true
|
encase.workspace = true
|
||||||
image.workspace = true
|
image.workspace = true
|
||||||
|
imageproc.workspace = true
|
||||||
nalgebra.workspace = true
|
nalgebra.workspace = true
|
||||||
notify-rust.workspace = true
|
notify-rust.workspace = true
|
||||||
open.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 egui::{Context, Ui};
|
||||||
|
use image::{GrayImage, Luma};
|
||||||
|
use imageproc::{distance_transform::Norm, morphology::Mask};
|
||||||
use rayon::iter::{ParallelBridge, ParallelIterator};
|
use rayon::iter::{ParallelBridge, ParallelIterator};
|
||||||
use tracing::info;
|
use tracing::{info, trace};
|
||||||
|
|
||||||
use crate::{app::App, ui::components::dragger_tip};
|
use crate::{app::App, ui::components::dragger_tip};
|
||||||
use goo_format::{File as GooFile, LayerDecoder, LayerEncoder};
|
use goo_format::{File as GooFile, LayerDecoder, LayerEncoder};
|
||||||
@@ -60,19 +67,29 @@ impl Plugin for ElephantFootFixerPlugin {
|
|||||||
x_radius, y_radius
|
x_radius, y_radius
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let intensity = self.intensity_multiplier / 100.0;
|
||||||
|
let darken = |value: u8| (value as f32 * intensity).round() as u8;
|
||||||
|
|
||||||
goo.layers
|
goo.layers
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.take(goo.header.bottom_layers as usize)
|
.take(goo.header.bottom_layers as usize)
|
||||||
.par_bridge()
|
.par_bridge()
|
||||||
.for_each(|layer| {
|
.for_each(|layer| {
|
||||||
let decoder = LayerDecoder::new(&layer.data);
|
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;
|
let erode = imageproc::morphology::grayscale_erode(&image, &Mask::square(10));
|
||||||
apply(&mut image, intensity, x_radius, y_radius);
|
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();
|
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)
|
new_layer.add_run(run.length, run.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,67 +107,3 @@ pub fn get_plugin() -> Box<dyn Plugin> {
|
|||||||
intensity_multiplier: 30.0,
|
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()
|
..Default::default()
|
||||||
},
|
},
|
||||||
first_layers: 10,
|
first_layers: 10,
|
||||||
|
transition_layers: 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
let file = File::open(FILE_PATH)?;
|
let file = File::open(FILE_PATH)?;
|
||||||
|
Reference in New Issue
Block a user