Evolving an image, part II - image rendering

The second installment in the series on evolving an image with trianges. In this post I will focus on the rendering of the actual image, both for rendering and scoring. Other parts of this series are:

  1. Make a generator for the genome.
  2. Translate genome to a rendered image.
  3. Score the rendered image and compare for evolution.
  4. (Optional) Possible optimizations.

Scoring generations is done by comparing their matrix representations. At the start of the project I started pretty much blank on the rendering of polygons in python. The Pillow package is primarily meant for graphical implementations, not matrix algebra. My path of drawing polygons on a canvas and then turning the canvas into a matrix representation could just as easily have been reversed. When everything was implemented, the performance wasn't optimal and I looked into mathematical generation of polygons with the shapely module. For now, let's focus on drawing polygons on the canvas.

python; genomeGenerator.py
from PIL import Image, ImageDraw
import numpy as np

...

def renderDna(dna):
    # initialize an image
    image = Image.new("RGB", (width, height))
    draw = ImageDraw.Draw(image,'RGBA')
    # draw polygons on image
    for item in dna:
        p0 = item[0]; p1 = item[1]; p2 = item[2]
        r = item[3]; g = item[4]; b = item[5]; a = item[6]
        color = (r, g, b, a)
        draw.polygon([p0, p1, p2], fill = color)
    # make unaffected background transparent
    image = image.convert('RGBA')
    dataPoints = image.getdata()
    newData = []
    for item in dataPoints:
        # if the pixel is black (0,0,0,_), make it transparent.
        if item[0] == 0 and item[1] == 0 and item[2] == 0:
            newData.append((0,0,0,0))
        else:
            newData.append(item)
    image.putdata(newData)
    # calculate matrix to do calculations with
    imageMatrix = np.array(image)
    imageMatrix = imageMatrix.astype(int)
    return [image, imageMatrix]

So what's happening here? First of, there is initializing a blank canvas for the polygons to be drawn upon. I expected to be able to initialize a canvas with an alpha channel (RGBA), but the pillow package for whatever reason doesn't allow this. We can however draw RGBA polygons on a RGB canvas, so this was just a matter of figuring out some idiosyncrasies. The default background now is black, so later on we will correct for this.

Next the actual polygons are being drawn. As said earlier, at the moment only polygons with 3 vertices can be drawn. The list of lists is iterated over, and each list within has a known layout of first 3 tuples for the coordinates, followed by 4 integers for the different colours.

After all the polygons have been drawn on a RGB canvas comes the 'hack' to make the background transparant. First we convert the type to a 4-channel tpye. Next any pixel that hasn't been affected by this rendering and remains black (that now has a value of (0, 0, 0, 255)) is made entirely transparant (0, 0, 0, 0).

Finally the matrix representation of the drawn image is generated with NumPy. Both the outputs are returned from the function in a list, so that depending on the need (either printing or using for calculations) one of the outputs can be used.

As a final remark I'd like to mention that the image preview functionality of Windows file explorer isn't accurate. As i was trying different setups to return my desired result, I was inspecting them with the image preview. Then I loaded an image in a browser, and the image shown wasn't the same as the preview. To be certain I opened the images in Gimp and verified the colour channels of test cases with python.

Comments

Popular posts from this blog

Quick and dirty image searches

Evolving an image, part I - genome generation