mirror of
https://github.com/visioncortex/vtracer.git
synced 2026-08-31 01:15:57 -07:00
Uncap watershed details
This commit is contained in:
+1
-1
@@ -27,7 +27,7 @@ homepage = "http://www.visioncortex.org/vtracer"
|
||||
repository = "https://github.com/visioncortex/vtracer/"
|
||||
|
||||
[workspace.dependencies]
|
||||
visioncortex = "0.9.1"
|
||||
visioncortex = "0.9.2"
|
||||
# Schneider curve fitting for the simplify pass. visioncortex pins an old
|
||||
# flo_curves internally for legacy reasons; we depend on the current one
|
||||
# directly and convert at the call boundary.
|
||||
|
||||
@@ -121,8 +121,8 @@ struct Args {
|
||||
adaptive_t: Option<f64>,
|
||||
|
||||
/// Watershed clustering: hierarchy cut level (0..=255, higher = more regions).
|
||||
#[arg(long, value_parser = clap::value_parser!(u8))]
|
||||
watershed_detail: Option<u8>,
|
||||
#[arg(long, value_parser = clap::value_parser!(u32))]
|
||||
watershed_detail: Option<u32>,
|
||||
}
|
||||
|
||||
fn parse_simplify_tolerance(s: &str) -> Result<f64, String> {
|
||||
|
||||
@@ -172,7 +172,7 @@ impl PyConfig {
|
||||
adaptive: bool,
|
||||
adaptive_window: u32,
|
||||
adaptive_t: f64,
|
||||
watershed_detail: u8,
|
||||
watershed_detail: u32,
|
||||
) -> PyResult<Self> {
|
||||
let palette = match palette {
|
||||
Some(list) => list.iter().map(|s| parse_hex(s)).collect::<PyResult<_>>()?,
|
||||
@@ -241,11 +241,11 @@ impl PyConfig {
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn watershed_detail(&self) -> u8 {
|
||||
fn watershed_detail(&self) -> u32 {
|
||||
self.inner.watershed_detail
|
||||
}
|
||||
#[setter]
|
||||
fn set_watershed_detail(&mut self, v: u8) {
|
||||
fn set_watershed_detail(&mut self, v: u32) {
|
||||
self.inner.watershed_detail = v;
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ pub struct SegmentKey {
|
||||
binary_adaptive: bool,
|
||||
binary_adaptive_window: u32,
|
||||
binary_adaptive_t: f64,
|
||||
watershed_detail: u8,
|
||||
watershed_detail: u32,
|
||||
}
|
||||
|
||||
/// High-level converter configuration. [`Config::build`] turns this into a
|
||||
@@ -133,7 +133,7 @@ pub struct Config {
|
||||
pub binary_adaptive_t: f64,
|
||||
/// Watershed clustering: where to cut the hierarchy (0..=255). Higher
|
||||
/// keeps more regions; 0 collapses the image to a single region.
|
||||
pub watershed_detail: u8,
|
||||
pub watershed_detail: u32,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
@@ -342,7 +342,9 @@ impl Config {
|
||||
// apart (within a just-noticeable difference) from surviving
|
||||
// as separate patches even at maximum detail.
|
||||
merge_diff: match self.clustering {
|
||||
Clustering::Watershed => ((255 - self.watershed_detail as i32) / 8).max(2),
|
||||
Clustering::Watershed => {
|
||||
(((255i64 - self.watershed_detail as i64) / 8).max(2)) as i32
|
||||
}
|
||||
_ => self.layer_difference,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -53,9 +53,10 @@ const ANCESTOR_AREA_BUDGET: usize = 3;
|
||||
/// Watershed frontend: hierarchical watershed by volume, cut at `detail`.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WatershedFrontend {
|
||||
/// Detail level (0..=255): where to cut the hierarchy. Each +25.5 roughly
|
||||
/// doubles the region count; 0 collapses the image to a single region.
|
||||
pub detail: u8,
|
||||
/// Detail level: where to cut the hierarchy. The normal range is 0..=255 —
|
||||
/// each +25.5 roughly doubles the region count, 0 collapses the image to a
|
||||
/// single region. Values above 255 are practically uncapped.
|
||||
pub detail: u32,
|
||||
/// Absorb regions smaller than this many pixels into their most
|
||||
/// color-similar neighbour after the cut (0 = keep all).
|
||||
pub min_area: usize,
|
||||
@@ -268,7 +269,7 @@ impl WatershedHierarchy {
|
||||
|
||||
/// Cut the hierarchy at `detail` and emit the stacked [`Segmentation`].
|
||||
/// Near-linear; safe to call repeatedly with different parameters.
|
||||
pub fn cut(&self, img: &ColorImage, detail: u8, min_area: usize) -> Segmentation {
|
||||
pub fn cut(&self, img: &ColorImage, detail: u32, min_area: usize) -> Segmentation {
|
||||
let (w, h) = (self.width, self.height);
|
||||
let n = w * h;
|
||||
let m = self.mst.len();
|
||||
@@ -279,7 +280,10 @@ impl WatershedHierarchy {
|
||||
// merge a little more). The persistence distribution is extremely
|
||||
// skewed — most merges are trivia at ≈ 0 — so the dial maps to a
|
||||
// region *count*, exponentially: every +25.5 of detail doubles the
|
||||
// target, from 1 region at 0 up to 1024 at 255.
|
||||
// target, from 1 region at 0. The target saturates at the edge count,
|
||||
// so a large detail (≥ 25.5·log2(pixels), e.g. ≥ 612 for a 4096²
|
||||
// image) is practically uncapped: λ = min persistence, keeping every
|
||||
// basin above the zero-persistence trivia.
|
||||
let mut uf = Uf::new(n);
|
||||
if m > 0 {
|
||||
let target = (2f64).powf(detail as f64 / 25.5).round() as usize;
|
||||
|
||||
@@ -78,7 +78,7 @@ fn regions(seg: &Segmentation) -> usize {
|
||||
#[test]
|
||||
fn flat_image_is_one_region() {
|
||||
let img = image(24, 16, |_, _| (90, 120, 150));
|
||||
for detail in [0u8, 128, 255] {
|
||||
for detail in [0u32, 128, 255] {
|
||||
let seg = WatershedFrontend {
|
||||
detail,
|
||||
min_area: 0,
|
||||
@@ -127,7 +127,7 @@ fn detail_is_monotone() {
|
||||
(r, g, 128)
|
||||
});
|
||||
let mut prev = 0usize;
|
||||
for detail in [0u8, 64, 128, 192, 255] {
|
||||
for detail in [0u32, 64, 128, 192, 255] {
|
||||
let seg = WatershedFrontend {
|
||||
detail,
|
||||
min_area: 0,
|
||||
@@ -202,7 +202,7 @@ fn hierarchy_recut_matches_one_shot() {
|
||||
(((x * 5 + y * 3) % 200) as u8, ((x / 8) * 30) as u8, ((y / 8) * 40) as u8)
|
||||
});
|
||||
let hierarchy = WatershedHierarchy::build(&img).unwrap();
|
||||
for detail in [64u8, 128, 200] {
|
||||
for detail in [64u32, 128, 200] {
|
||||
let recut = hierarchy.cut(&img, detail, 16);
|
||||
let one_shot = WatershedFrontend {
|
||||
detail,
|
||||
@@ -231,7 +231,7 @@ fn session_recut_matches_one_shot() {
|
||||
clustering: Clustering::Watershed,
|
||||
..Config::default()
|
||||
};
|
||||
for detail in [128u8, 200, 64] {
|
||||
for detail in [128u32, 200, 64] {
|
||||
let cfg = Config {
|
||||
watershed_detail: detail,
|
||||
..base.clone()
|
||||
|
||||
Reference in New Issue
Block a user