Thursday, February 24, 2011

More SVG - trace from bitmap with Inkscape and color editing

I'm attempting to convert some of the pysanky eggs I drew with POV-ray to SVG so that I can scale them freely.  Also, once they're converted, I would like to be able to edit their colors in the same way I did with the Python logo in my last post.

To the best of my knowledge, Inkscape (potrace) is the most readily available tool for converting the bitmap images from POV-ray to SVG.  There are a few things to look out for, but this is still really easy:

1) Remove all reflection and shading from the textures (colors) in the POV-ray scene.  Work on getting even lighting for the whole scene.  Generate the scene without antialiasing (jagged edges are fine, and even desired).

2) Follow the directions here for tracing the SVG drawing.

Now to use the standard library's xml.etree.ElementTree package to get at the colors.  Before jumping in, I first removed any references to the old bmp image from POV-ray and checked on the three color values in the SVG file.

The three colors in the image are #fefefe (slightly off white), #000000 (black), and #00fd00 (light green).


Manipulation of colors in an SVG image.

from xml.etree import ElementTree as ET

def drilldown(nodex, oldcolor, newcolor):
    Walk node of element tree in search
    of oldcolor.

    Replace oldcolor with newcolor where
    oldcolor occurs.
    childrenx = nodex.getchildren()
    if childrenx:
        for childx in childrenx:
            drilldown(childx, oldcolor, newcolor)
        itemsx = nodex.items()
        for itemx in itemsx:
            if itemx[1].find(oldcolor) > -1:
                oldcolorstr = itemx[1]
                newcolorstr = oldcolorstr.replace(oldcolor, newcolor)
                nodex.set(itemx[0], newcolorstr)

def changecolor(oldcolor, newcolor, filename):
    Replaces oldcolor with newcolor in an
    SVG file named filename.

    All three arguments are strings.

    oldcolor and newcolor are hex strings
    in the format #xxxxxx.
    # the colors are at the lowest level
    # in the XML (SVG) file
    # drill down until they are reached
    svgobj = ET.parse(filename)
    rootx = svgobj.getroot()
    drilldown(rootx, oldcolor, newcolor)

$ python2.5
Python 2.5.4 (r254:67916, Aug  9 2010, 08:57:51)
[GCC 4.2.1 20070719 ] on openbsd4
Type "help", "copyright", "credits" or "license" for more information.
>>> import svgchangecolor as svgcc
>>> svgcc.changecolor('#00fd00', '#ffff00', 'crossdroppullegg.svg')
>>> svgcc.changecolor('#000000', '#00aa00', 'crossdroppullegg.svg')
>>> svgcc.changecolor('#fefefe', '#000000', 'crossdroppullegg.svg')


There are only three elements with color in the file (the dark green one is doing all the "work" of filling in the pattern).  The script is really overkill in this case.  For a drawing with a number of separate paths with the same color, though, this could be very useful.


  1. Carl,

    I've been following these posts with interest. It's interesting to see Python used to create and manipulate images in such unique ways.

    One suggestion I would have for your color replace script would be to consider a literate-style interface, where svgcc returns another svgcc object at the end of every operation, so you can chain them together. Also I prefer to work with the XML tree in memory (if small enough) and only do one read and write of the file. Other than those critiques, very cool stuff!

  2. @swheatly -thanks for the suggestions. I probably won't get to this post in time, but I will look up literate interface and attempt to adjust my coding style accordingly.
    Again, thanks.