Skip to content

feat: V2 stacking pipeline with OKLab MMCQ, depth ordering, and comprehensive optimization#3

Merged
Neroued merged 11 commits into
masterfrom
feat/v2-pipeline
Mar 26, 2026
Merged

feat: V2 stacking pipeline with OKLab MMCQ, depth ordering, and comprehensive optimization#3
Neroued merged 11 commits into
masterfrom
feat/v2-pipeline

Conversation

@Neroued
Copy link
Copy Markdown
Owner

@Neroued Neroued commented Mar 26, 2026

Summary

V2 层叠模型管线的完整实现,从颜色量化到 SVG 输出的端到端矢量化流程,以及全面的代码质量和性能优化。

V2 管线核心功能

  • OKLab MMCQ 颜色量化:感知均匀色彩空间中的中位切分量化,自动 K 值检测
  • 深度排序:基于遮挡关系的拓扑排序(Kahn + Tarjan SCC 打环),画家算法渲染
  • 形状延伸:膨胀到被遮挡区域消除接缝
  • 逐层 Potrace 追踪:独立位图追踪每个连通域
  • 路径优化:近线性段合并、链式段合并
  • 同色合并:z-order 安全的相邻同色形状合并
  • 覆盖率修补:检测并补全未覆盖像素区域

质量评估

  • 像素保真度指标(PSNR、SSIM、Delta E、覆盖率)
  • 边缘保真度指标(Chamfer 距离、Edge F1)
  • 路径结构指标(形状数、碎片率、Gini 系数)
  • 基线对比和回归检测
  • Manifest 批量评估模式

性能优化(4.18x 整体加速)

  • 算法复杂度:ExtractShapeLayers O(L×WH) → O(WH),ConsolidatePalette Kruskal 单遍
  • OpenMP 并行(MSVC 2.0 兼容):像素行并行、层级 Potrace 并行、路径优化并行、Coverage guard 并行
  • 内存优化:SrgbToOklab 单遍缓存、ROI 级膨胀、priority_queue → make_heap、累加数组复用
  • 缓存优化:SrgbToLinear 256 条目 LUT、逐标签 mask 缓存

代码质量

  • 全面的代码风格统一(PascalCase 函数、snake_case 变量)
  • 消除魔法数字、冗余注释、日志前缀
  • 公共 API / 内部实现严格分离
  • 文档、Python 绑定、CLI 工具与 V2 管线完全同步

Test plan

  • 单元测试全部通过(3/3)
  • V2 管线 15 张测试图 eval 分数 69.3(零回归)
  • 性能基准:优化前 94.9s → 优化后 22.7s(4.18x 加速)
  • Release 构建 -O3 验证
  • CLI --help 所有参数正确显示
  • Python PipelineMode 可正常导入导出

Made with Cursor

Neroued added 11 commits March 26, 2026 16:12
…hape extension

V2 pipeline implementation (pipeline_v2.cpp) with:
- OKLab MMCQ color quantization (src/quantize/)
- Connected-component shape layers with depth ordering (src/stacking/)
- Shape extension via morphological dilation into occluded regions
- Per-layer Potrace tracing with path optimization (src/curve/path_optimize)
- Z-order safe same-color shape merging
- Diagnostic depth order validation logging

Supporting changes:
- PipelineMode enum and pipeline_mode config (config.h)
- --pipeline CLI flag for raster_to_svg and evaluate_svg
- Python bindings for V2 pipeline mode
- is_hole flag on BezierContour for correct SVG winding
- Eval: border_delta_e_mean metric, updated manifest
- Research docs and cursor rules

Made-with: Cursor
…acing

Keep labels alive past shape tracing so ApplyCoverageGuard can identify
uncovered regions.  Patch shapes are rotated to the bottom of the z-order
so they only fill through gaps without overpainting correct content.

Made-with: Cursor
Depth ordering improvements:
- Replace convex hull with morphological dilation for D(i,j) signal
- Implement Tarjan SCC for efficient batch cycle breaking
- Add adaptive delta threshold based on D(i,j) distribution (P25)
- Improve background detection with border pixel frequency voting
- Add significance filtering for small fragment pairs

Eval rasterizer fix:
- Replace heuristic hole detection in FillShapeWithHoles with correct
  SVG evenodd fill via XOR, matching browser rendering behavior

Made-with: Cursor
- Increase K-Means refinement from 3 to 8 iterations with early
  convergence detection to produce more accurate centroids
- Add post-MMCQ palette consolidation: greedily merge centroid pairs
  within OKLab distance < 0.025 to reduce over-segmentation
- Add spatial label smoothing via 5x5 majority vote filter with color
  distance guard to eliminate salt-and-pepper noise at boundaries

Made-with: Cursor
- Include Potrace-detected holes in VectorizedShape with is_hole flag,
  filtered by min_hole_area to avoid tiny artifacts. This fixes incorrect
  solid fill where shapes should have transparent regions (eval +1.3 pts).
- Cap path optimization chain-merge at 3 segments to prevent a single
  cubic from representing too many original segments, which caused visible
  dents on smooth arcs.
- Increase TryMergeSegments sample density from 8 to 12 per segment for
  more sensitive deviation detection.
- Tighten merge_eps lower bound from 0.5 to 0.3 and coefficient from
  0.6 to 0.5 for better curve fidelity.

Eval: aggregate 68.0 -> 69.3 (+1.3). Notable gains: s_64c9a +11.5,
c_5bf9c +5.5, tjls +3.6. One regression: c_193ce -3.8 (depth ordering
errors exposed by holes).

Made-with: Cursor
Phase 1: Remove dead code (CurveSegment, AngleBetween, FitBezierOnGraph,
3 boundary helpers, 7 assembly functions), invalid code (median_idx no-op),
duplicate includes, and unnecessary includes.

Phase 2: Extract shared utilities and eliminate duplication:
- Unify Bezier eval functions and MakeLinearBezier across curve module
- Extract LabDistSq/LabDist to cv_utils.h, replace 4 duplicate impls
- Deduplicate OKLab SrgbToOklab via shared LinearRgbToOklab
- Extract FindNearestCentroid in color_quantize.cpp
- Deduplicate Potrace module (RingToBezier, RunPotraceTrace, SimplifyRing)
- Extract MakeStrokeShape in thin_line.cpp
- Extract ApplySubiteration in morphology.cpp ZhangSuenThinning

Phase 3: Split long functions into focused sub-functions:
- QuantizeColors -> RunMmcq + RefineCentroidsKMeans + ConsolidatePalette + SmoothLabels
- ComputeDepthOrder -> BuildDepthGraph + AddContainmentEdges + TopologicalSortWithCycleBreaking
- FindBackground -> CollectBorderInfo + SelectBackground
- EstimateOptimalColors -> BuildColorSamples + ScoreCandidateK
- ApplyCoverageGuard -> FindCoverageGaps + PatchMissingRegions
- Extract pipeline shared logic (DeriveTraceParams, RescaleShapes, ClampShapesToBounds)
- Extract VectorizeImpl from 3 Vectorize overloads

Phase 4: Naming and style unification:
- Rename ColorKey lambda to snake_case
- Rename HistBin members to snake_case (sum_l, sum_a, etc.)
- Unify V1 pipeline log prefixes to "V1: ..." format
- Normalize same-module include paths (remove redundant subdir prefixes)

Phase 5: Decouple VectorizedShape into src/detail/vectorized_shape.h,
eliminating reverse dependencies from contour/curve/trace -> output.

Made-with: Cursor
P0 - Hot path fixes:
- Eliminate per-pixel heap allocation in SmoothLabels (freq vector reuse)
- Cache OKLab values in ColorGrid::Build (single SrgbToOklab pass instead of 3)
- Rewrite ExtractShapeLayers as single-pass O(WH) BFS (was O(L*WH))
- Cache per-label masks in V1 pipeline (eliminate repeated full-image compare)

P1 - OpenMP parallelization (MSVC OpenMP 2.0 compatible):
- Row-level parallel: pixel label assignment, SmoothLabels, ColorGrid histogram
- Layer-level parallel: Potrace tracing in both V1 and V2 pipelines
- Shape-level parallel: path optimization (OptimizeShapePaths)
- Component-level parallel: coverage guard patch generation
- V2 depth validation reduction(+:mismatch)

P2 - Memory optimization:
- Coverage guard: ROI-level masks instead of full-image allocation
- ExtendShapeMasks: ROI-bounded dilation, pixel count tracker
- ColorBox: replace priority_queue with make_heap/pop_heap (avoid deep copy)
- RefineCentroidsKMeans: accumulator array reuse across iterations

P3 - Algorithm & cache optimization:
- SrgbToLinear: 256-entry runtime LUT for uint8 fast path
- V2 depth validation: opt-in via enable_depth_validation config (default off)
- ConsolidatePalette: Kruskal-style pre-sorted union-find (single pass)
- EstimateOptimalColors: parallel candidate K evaluation

All OpenMP pragmas use int loop variables and #ifdef _OPENMP guards
for MSVC compatibility. Eval score unchanged at 69.3 (zero regression).

Made-with: Cursor
- Export PipelineMode in Python __init__.py and __all__
- Add enable_depth_validation to PartialVectorizerConfig and MergeInto
- Add --enable-depth-validation CLI flag to raster_to_svg and evaluate_svg
- Fix V2 pipeline hardcoded stroke=false, now respects config
- Add coverage guard step to AGENTS.md V2 flow diagram
- Add V1/V2 applicability column to README config parameter table
- Add coverage guard to README V2 feature list

Made-with: Cursor
- Add cp314-* to cibuildwheel build targets
- Add Python 3.14 to CI test matrix and pyproject.toml classifiers
- Split Windows wheel builds into 3 parallel jobs (310-311, 312-313, 314)
  to reduce wall time (~3x speedup, matching neroued_3mf approach)
- Upgrade cibuildwheel from v2.23 to v3.4.0
- Simplify macOS wheel to arm64-only (cross-compile x86_64 unreliable)
- Use per-matrix cibw_build + id for clearer artifact naming
- Update README supported Python versions to 3.10-3.14

Made-with: Cursor
M_PI is a POSIX extension not defined by MSVC unless _USE_MATH_DEFINES
is set. Use C++20 std::numbers::pi instead.

Made-with: Cursor
Add detailed MSVC-specific rules to prevent future issues:
- Forbid M_PI and other POSIX math constants (use std::numbers)
- Forbid constexpr with non-constexpr math functions on MSVC
- Document OpenMP 2.0 limitations (int loop vars, no collapse/task)
- Document POSIX function equivalents (_popen, localtime_s, etc.)
- Forbid VLA and GCC/Clang extensions

Made-with: Cursor
@Neroued Neroued merged commit 8804ee5 into master Mar 26, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant