: Convert PNG pixel arts to SVG without tracing For a personal project, I designed some icons as PNG files and would like to export them as SVGs. I know the next question: I don't want a raster,
For a personal project, I designed some icons as PNG files and would like to export them as SVGs.
I know the next question: I don't want a raster, I need vectors. I found this question but since it's based on InkScape, I suppose this will involve tracing.
I do not want that. What I am hoping for is more like pixel2svg proposes. Actually, pixel2svg is almost perfect and gives the sort of thing I am looking for.
From the doc:
static.florian-berger.de/tango-heart-inkscape.png
Here is one example from my project.
Only caveat: some of my icons require an intermediary alpha value. pixel2svg appears to be quite binary: either there is a pixel, either it is transparent. Nothing in between. From my project, one of the problematic examples in PNG and in SVG.
I tried to look for a contact address to see if this could be changed but I did not find one.
Does anyone know of a solution to generate a pixelated SVGs from (relatively) small (and monochrome, if this helps) PNGs?
To sum my requirements up
Vector, no raster
Pixel by pixel conversion, no tracing
Take alpha (with intermediate values, not only 0/1) into account
Optional: Command line would be great, as it would allow for batch conversion
Edit: enhanced version of pixel2svg
Based on JohnB's answer, I contacted the original author to submit some changes (typically change RGB to RGBA when alpha is neither 0 nor 255). I have had no answer yet so I created a Github repo with the changes to make them available to anyone.
Since, hey, it's Github! Python developers should feel free to fork and make it better (pull requests would help me know Python better). The next big challenge is to group pixels the same colors as the same vectors (doing it in InkScape considerably reduced the SVG size despite InkScape adding much custom information).
More posts by @Hamaas979
3 Comments
Sorted by latest first Latest Oldest Best
If you have python and PIL/Pillow, I just wrote this (a trivially simple version of the tool in @JohnB 's answer):
#!/usr/bin/env python2.7
import sys
infile = sys.argv[1]
outfile = sys.argv[2]
from PIL import Image
image = Image.open(infile).convert('RGBA')
data = image.load()
out = open(outfile, "w")
out.write('<?xml version="1.0" encoding="UTF-8" standalone="no"?>n')
out.write('<svg id="svg2" xmlns="http://www.w3.org/2000/svg" version="1.1" width="%(x)i" height="%(y)i" viewBox="0 0 %(x)i %(y)i">n' % {'x':image.size[0], 'y':image.size[1]})
for y in range(image.size[1]):
for x in range(image.size[0]):
rgba = data[x, y]
rgb = '#%02x%02x%02x' % rgba[:3]
if rgba[3] > 0:
out.write('<rect width="1" height="1" x="%i" y="%i" fill="%s" fill-opacity="%.2f" />n' % (x, y, rgb, rgba[3]/255.0))
out.write('</svg>n')
out.close()
Call it with arguments input.png output.svg and it should go. I wouldn't try it with anything bigger than a few hundred pixels squared though.
pixel2svg has the potential to do this, but it requires some modification of the script.
How to do it
You need to modify the svgdoc.add call on line 125 of pixel2svg.py to add in opacity attribute. It should look like the following:
svgdoc.add(svgdoc.rect(insert = ("{0}px".format(colcount * arguments.squaresize),
"{0}px".format(rowcount * arguments.squaresize)),
size = ("{0}px".format(arguments.squaresize + arguments.overlap),
"{0}px".format(arguments.squaresize + arguments.overlap)),
fill = svgwrite.rgb(rgb_tuple[0],
rgb_tuple[1],
rgb_tuple[2]),
opacity = rgb_tuple[3]/float(255)))
Here's a diff of the code change. To show it in action I made this very tiny image. Here's the SVG produced by the modified pixel2svg (or check it out in action here.):
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink" baseProfile="full" height="40px" version="1.1" width="440px">
<defs/>
<rect fill="rgb(255,0,255)" height="40px" opacity="0.101960784314" width="40px" x="40px" y="0px"/>
<rect fill="rgb(255,0,255)" height="40px" opacity="0.2" width="40px" x="80px" y="0px"/>
<rect fill="rgb(255,0,255)" height="40px" opacity="0.301960784314" width="40px" x="120px" y="0px"/>
<rect fill="rgb(255,0,255)" height="40px" opacity="0.4" width="40px" x="160px" y="0px"/>
<rect fill="rgb(255,0,255)" height="40px" opacity="0.501960784314" width="40px" x="200px" y="0px"/>
<rect fill="rgb(255,0,255)" height="40px" opacity="0.6" width="40px" x="240px" y="0px"/>
<rect fill="rgb(255,0,255)" height="40px" opacity="0.701960784314" width="40px" x="280px" y="0px"/>
<rect fill="rgb(255,0,255)" height="40px" opacity="0.8" width="40px" x="320px" y="0px"/>
<rect fill="rgb(255,0,255)" height="40px" opacity="0.901960784314" width="40px" x="360px" y="0px"/>
<rect fill="rgb(255,0,255)" height="40px" opacity="1.0" width="40px" x="400px" y="0px"/>
</svg>
What's going on
pixel2svg uses PIL to process through each pixel of the supplied image. It actually already pulls the alpha value of each pixel as an integer [0-255], it just doesn't do much with it:
image = PIL.Image.open(positional[0])
print("Converting image to RGBA")
image = image.convert("RGBA")
The pixel data is then reconstructed into an SVG using the svgwrite library. Each pixel is drawn using as the svgwrite.shapes.Rect method which allows you to chain "additional SVG attributes as keyword-arguments". The SVG opacity value expects a float [0.0-1.0], so we just need to normalize the alpha value before setting it as the opacity.
Disclaimer: This was my first time ever messing with Python, so feel free to point out any beginner errors I may have made!
If you do not auto trace, the option is do it by hand. Make a grid and start drawing boxes.
Your requirements:
Vector, no raster (OK)
Pixel by pixel conversion, no tracing (OK) half ok. A vector based file has no pixels.
Take alpha into account (OK) If you simply do not draw a background.
Optional: Command line would be great. Any comand line option is a "tracing" Good that is one is optional.
Svg can also include png inside it... but it has not much sense in this case I belive.
Terms of Use Create Support ticket Your support tickets Stock Market News! © vmapp.org2024 All Rights reserved.