Skip to content
github-actions[bot] edited this page Oct 17, 2026 · 9 revisions

SvgCreator technical notes

SvgCreator turns a raster image, such as an anime character illustration, into a line-art SVG: black ink outlines with no colouring. These pages record the techniques used in the project, why each one was chosen over the alternatives, and the pitfalls hit along the way, with the measurements that drove each decision.

These pages are generated from docs/ in the repository, which is the source of truth. Edit them there; direct wiki edits are overwritten on the next sync.

Pipeline

flowchart LR
    A[Image file] --> B[Decode<br/>SkiaSharp]
    B --> C[Extract ink<br/>DoG + hysteresis<br/>+ Lab colour edges]
    C --> D[Label components<br/>union-find]
    D --> E[Trace contours<br/>pixel-edge following]
    E --> F[Fit shapes & curves<br/>ellipses, corners + Schneider]
    F --> G[Write SVG<br/>compact relative paths]
Loading
Stage Page Code
Extract ink Line Extraction, Colour Edges src/SvgCreator/Imaging/
Label components Connected Components Tracing/ComponentLabeler.cs
Trace contours Contour Tracing Tracing/ContourTracer.cs
Fit curves Curve Fitting Fitting/
Recognize circles and ellipses Shape Recognition Fitting/EllipseFitter.cs
Write SVG SVG Output Svg/SvgWriter.cs
Optional stamp / postmark look Stamp and Postmark Styles Svg/SvgStyle.cs, Svg/StampFilter.cs, Svg/PostmarkCancellation.cs
Postage stamp (separate generator) Postage Stamp PostageStampGenerator.cs, Svg/PostageStampWriter.cs
Airmail envelope (separate generator) Envelope EnvelopeGenerator.cs, Svg/EnvelopeWriter.cs
Envelope + stamp + postmark, composed Mail Piece MailPieceGenerator.cs, Composition/MailPieceComposer.cs

Cross-cutting topics:

Design principles that emerged

  1. Measure before fixing. Several obvious explanations turned out to be wrong. Probing actual pixel values or filter responses found the real cause each time; see Debugging Techniques.
  2. Prefer evidence over prediction. For example, close gaps by following faint lines that really exist in the image (hysteresis) before inventing geometry.
  3. Output is filled shapes, not strokes. Each ink region is a filled path under fill-rule="evenodd", which keeps the original line weight. Most later choices follow from this.
  4. Everything is O(pixels) over flat arrays. No reflection and no per-pixel allocation, which suits Native AOT and Lambda.

Clone this wiki locally