r/DSP • u/readilyaching • 6d ago
I built an open-source image-to-SVG vectorization library -the interesting parts turned out to be classic DSP problems
Over the past year I've been building Img2Num, an open-source C++ library that converts raster images into SVGs, with Python, JavaScript, and C bindings.
The motivation: existing vectorization tools are really built for line art, logos, and scans - clean inputs with hard edges. I wanted something that could handle natural images (photos, textures, noisy real-world content), and that turns out to be a very different problem. You can't just trace what's there, because what's there is full of sensor noise, JPEG artifacts, and gradients that explode into thousands of junk paths. So the vectorization step ends up mattering less than the signal processing in front of it.
The rough pipeline: edge-preserving denoising with a bilateral filter (selectable between RGB and CIELAB - perceptual color spaces make a real difference in how edges survive), k-means color quantization, Suzuki-Abe contour tracing, and then Savitzky-Golay smoothing applied to the traced contours. That last step was the fun one: treating a closed contour as a pair of periodic 1D signals (x(t), y(t)) and filtering them means you can smooth out pixel staircase noise while preserving corners far better than naive moving averages, and SG's polynomial fitting is a good match for that.
The part I'm still iterating on is adaptive preprocessing - estimating noise per image (wavelet MAD estimator) and tuning the denoising strength accordingly, so the traced regions stay stable instead of speckle turning into hundreds of junk paths.
Everything is on GitHub and installable via pip and npm (both "img2num"), docs at img2num.dev. I would genuinely love feedback from this crowd (the DSP crowd), especially on the smoothing and noise estimation choices - I came at this from the software side and learned the DSP as I went on.
5
u/SHFTD_RLTY 6d ago
This looks pretty awesome and is something I needed at work for quite some time but as you mentioned, existing solutions had major issues.
If I have the time, I'll try it out today, we modernized an iconset from Win XP to a modern, flat design using AI but are still stuck with pixel graphics. Most free tools for this use-case rely on strokes which doesn't work well with some of the symbols.
I'll update the response once I had the time to try it out.
2
u/readilyaching 5d ago
Hi.
That sounds great - it would be wonderful to get a new user for the library.
Currently, I don't think that it quite matches your intended image type - synthetic (computer generated; e.g., icons, logos) - so the results might not be as great. We currently use K-Means clustering to simplify natural images, but it currently applies to all images, so it might be a problem for you.
We do have an open pull request that adds support for synthetic images (https://github.com/Ryan-Millard/Img2Num/pull/497), but it takes a while to get these types of changes ready because we need to ensure that they work properly. I'm hoping to get it ready in like a month or two.
3
u/trele_morele 6d ago
> That last step was the fun one: treating a closed contour as a pair of periodic 1D signals (x(t), y(t)) and filtering them means you can smooth out pixel staircase noise while preserving corners far better than naive moving averages, and SG's polynomial fitting is a good match for that.
How did you figure out the optimal frequency band for the smoothing? From empirical data or analytic methods?
3
u/readilyaching 6d ago edited 5d ago
SG filters aren't specified by a cutoff directly — you pick window length and polynomial order, and those imply a lowpass response. We (more specifically, @Krasner, the maintainer who implemented it) swept window/order pairs against test images, looking for where staircase noise disappeared without corners rounding off. The staircase artifact lives at a pretty narrow, predictable band (1-2 px of arc length), well separated from real shape features, so the tuning was forgiving.
1
u/antiduh 6d ago
What's the motivation for something like this? Are there use cases where you simply need an svg, instead of a raster format like png? As a strict alternative to png, it seems like the quality is low, so I imagine there had to be some other driving factor.
3
u/SHFTD_RLTY 6d ago
I explained a use-case in another reply. Basically an iconset only available as pixel graphics needs to be converted to SVG to allow UI scaling for accessibility reasons.
2
u/readilyaching 5d ago
Vectors (SVGs) and rasters (like PNGs) are in different realms, so that is completely understandable.
Vector images are great because they are mathematically derived (defined by functions that express how to draw their lines). They are typically best when you don't want to lose quality when zooming in and out of the image - unlike rasters, their maths ensures that their edges remain fine and sharp.
Raster images work like how printers work - they are a bunch of dots in a plane (the boundaries of the image) with specific coordinates (x and y; kind of like plotting on a cartesian plane, except every coordinate has a value). The problem with raster images is that they lose quality when zoomed-in or out because they are discrete representations (there is nothing between each pixel; e.g., nothing lies between (x=1;y=2 and x=2;y=2) because those coordinates don't work on fractions).
With the way that Img2Num transforms images into SVGs, they do get worse when it comes to quality - it is a lossy program. That's a non-negotiable tradeoff because an SVG with extremely fine details relies on a lot of computations (the computer has to calculate how to draw each function - which defines the shapes - defined inside the image). As a result, Img2Num has to remove tiny less-significant details to avoid extremely large amounts of computations in quick succession when trying to render the SVG (Img2Num does not render the SVG - your computer does it and has standards for rendering them; Img2Num must follow those standards).
If you have a good computer, you can play around with the processing settings on our React.js color-by-number example app to see how it works - you can even reduce the blur and increase the number of colors in the final product if you like. I don't recommend using a large images when playing around because it will be slow (some images have millions of nunber that Img2Num will need to process).
I hope this helped. :)
1
u/bushed_ 6d ago
I don't get it, why?
The output image isnt scale-able at all?
1
u/readilyaching 5d ago
I'm not quite sure what you mean by that, so I'm going to make an assumption and try to answer it. Please will you help me understand if I'm wrong.
The output is real vector paths, so it scales infinitely in the sense that edges stay crisp at any size - but you're right that it's not magic upscaling. It can't recover detail that isn't in the source - that would likely take much more processing power. What you get is a posterized/stylized representation of the image, a bit like a screen print, with however many color layers you configure.
That's the actual use case: stylized art from photos, posters that print clean at any size, laser cutting/plotting, color-by-number generation (the original motivation), and getting editable geometry you can manipulate in Illustrator/Inkscape instead of pixels. If you want a photorealistic image at higher resolution, an ML upscaler is the right tool - this is for when you want vector output specifically.
2
u/bushed_ 5d ago
yeah great for a t shirt design, printing, cutting, or something but isn’t that just what a rasterization process does? the at any size is generous imo?
not as in the weeds on the specific math, but the example image didn’t look great blown up imo. not trying to be a hater though
1
u/readilyaching 5d ago
That's fair - I know it doesn't work well on every image with the default settings. The library is also quite young still, so there is lots of area for improvement.
1
u/bushed_ 5d ago
yeah i’m not perhaps the most in the know so take my comments with a grain of salt. still a cool project!
1
u/readilyaching 4d ago
Compare these images (thank to NASA) by zooming into them and pay attention to the edges in both - the SVG has crisper edges and the black is a uniform color.
Raster: https://images.nasa.gov/details/PIA01481 SVG: https://img2num.dev/img/homepage-demo.svg
Some other good images to look at are here: https://github.com/Ryan-Millard/Img2Num/blob/main/README.md
Did you see the color-by number example I built with it to showcase cool things you can do?
6
u/OMGCluck 6d ago
I have indeed seen that in many image-to-SVG converters posted on reddit. I find the banding in output results kinda ruins the peripheral drift effect when converting this image: https://goodo.nya.je/toroid-99.avif
One day I will manually trace it when I have the time, using
<ellipse>a lot with native SVG gradients and filters afterwards to make the end result work, but I'm curious to see if you're able to flatten the colours beforehand using this tool to prevent the banding?