Here's an interesting bit of Python code I hacked together – it's a script that takes an image and warps it so that it is tileable (making it suitable for a repeating backgound or a texture in a game).

A fractal

A Mandlebrot fractal

If you use it on a photograph, it will come out looking like a fair-ground mirror. But it works well when applied to a pattern, or something more abstract, such as the fractal image on the left.

The code is public domain – use it for whatever the heck you want!

Example Output

Update: Here's another, more interesting example, The original is here.

The Code

import Image
from math import *

def maketilable(src_path, dst_path):
    src = Image.open(src_path)
    src = src.convert('RGB')
    src_w, src_h = src.size

    dst = Image.new('RGB', (src_w, src_h))
    w, h = dst.size

    def warp(p, l, dl):
        i = float(p) / l
        i = sin(i*pi*2 + pi)
        i = i / 2.0 + .5
        return abs(i * dl)

    warpx = [warp(x, w-1, src_w-1) for x in range(w)]
    warpy = [warp(y, h-1, src_h-1) for y in range(h)]

    get = src.load()
    put = dst.load()

    def getpixel(x, y):

        frac_x = x - floor(x)
        frac_y = y - floor(y)

        x1 = (x+1)%src_w
        y1 = (y+1)%src_h

        a = get[x, y]
        b = get[x1, y]
        c = get[x, y1]
        d = get[x1, y1]

        area_d = frac_x * frac_y
        area_c = (1.-frac_x) * frac_y
        area_b = frac_x * (1. - frac_y)
        area_a = (1.-frac_x) * (1. - frac_y)

        a = [n*area_a for n in a]
        b = [n*area_b for n in b]
        c = [n*area_c for n in c]
        d = [n*area_d for n in d]

        return tuple(int(sum(s)) for s in zip(a,b,c,d))


    old_status_msg = None
    status_msg = ''
    for y in xrange(h):

        status_msg = '%2d%% complete' % ((float(y) / h)*100.0)
        if status_msg != old_status_msg:
            print status_msg
        old_status_msg = status_msg

        for x in xrange(w):
            put[x, y] = getpixel(warpx[x], warpy[y])

    dst.save(dst_path)


if __name__ == "__main__":

    import sys
    try:
        src_path = sys.argv[1]
        dst_path = sys.argv[2]
    except IndexError:
        print "<source image path>, <destination image path>"
    else:
        maketilable(src_path, dst_path)
This blog post was posted to It's All Geek to Me on Saturday July 18th, 2009 at 6:06PM
 

3 Responses to "Making tileable images with Python"

  • Pauli Virtanen
    July 18th, 2009, 9:37 p.m.

    Smells like a job for Numpy (http://www.scipy.org [scipy.org]):

    import Image
    import numpy as np

    def maketilable(src_path, dst_path, w=None, h=None):
    src_img = Image.open(src_path)
    src_img = src_img.convert('RGB')
    src = np.asarray(src_img)

    src_w, src_h, ncolors = src.shape

    if w is None:
    w = src_w
    if h is None:
    h = src_h

    def warp(p, l, dl):
    i = p * 1.0 / l
    i = np.sin(i*np.pi*2 + np.pi)
    i = i / 2.0 + .5
    return abs(i * dl)

    xp = warp(np.arange(w), w-1, src_w-1)[:,np.newaxis]
    yp = warp(np.arange(h), h-1, src_h-1)[np.newaxis,:]

    x = xp.astype(int)
    y = yp.astype(int)

    x1 = (x + 1) % src_w
    y1 = (y + 1) % src_h

    a = src[x, y]
    b = src[x1, y]
    c = src[x, y1]
    d = src[x1, y1]

    frac_x = (xp - np.floor(xp))[...,np.newaxis]
    frac_y = (yp - np.floor(yp))[...,np.newaxis]

    d *= frac_x * frac_y
    c *= (1.-frac_x) * frac_y
    b *= frac_x * (1. - frac_y)
    a *= (1.-frac_x) * (1. - frac_y)

    dst = a + b + c + d
    dst_img = Image.fromarray(dst, 'RGB')
    dst_img.save(dst_path)

    if __name__ == "__main__":
    import sys
    try:
    src_path = sys.argv[1]
    dst_path = sys.argv[2]
    except IndexError:
    print "<source image path>, <destination image path>"
    else:
    maketilable(src_path, dst_path)
  • July 18th, 2009, 9:49 p.m.

    Pauli, Nice! I think it is a job for Numpy.

  • Pauli Virtanen
    July 18th, 2009, 10:11 p.m.

    Once more, this time demonstrating the use of scipy.ndimage for the interpolation:

    import Image
    import numpy as np
    import scipy.ndimage

    def maketilable(src_path, dst_path, w=None, h=None):
    src_img = Image.open(src_path)
    src_img = src_img.convert('RGB')
    src = np.asarray(src_img)

    src_w, src_h, ncolors = src.shape

    if w is None:
    w = src_w
    if h is None:
    h = src_h

    def warp(p, l, dl):
    i = p * 1.0 / l
    i = np.sin(i*np.pi*2 + np.pi)
    i = i / 2.0 + .5
    return abs(i * dl)

    x = warp(np.arange(w), w-1, src_w-1)
    y = warp(np.arange(h), h-1, src_h-1)

    coords = np.broadcast_arrays(*np.ix_(x, y))

    dst = np.empty((w, h, 3), src.dtype)
    for j in xrange(3):
    dst[:,:,j] = \
    scipy.ndimage.map_coordinates(src[:,:,j],
    coords,
    mode='wrap',
    order=3)

    dst_img = Image.fromarray(dst, 'RGB')
    dst_img.save(dst_path)

    if __name__ == "__main__":
    import sys
    try:
    src_path = sys.argv[1]
    dst_path = sys.argv[2]
    except IndexError:
    print "<source image path>, <destination image path>"
    else:
    maketilable(src_path, dst_path)

Leave a Comment

You can use bbcode in the comment: e.g. [b]This is bold[/b], [url]http://www.willmcgugan.com[/url], [code python]import this[/code]
Preview Posting...
Previewing comment, please wait a moment...

My Tweets

Will McGugan

My name is Will McGugan. I am an unabashed geek, an author, a hacker and a Python expert – amongst other things!

Search for Posts
Possibly related posts
Tags
Popular Tags
 
Archives
2013
 
Recent Comments
Hey Will thanks for the information. I will keep that in mind.
Hey Will thanks for the information. I will keep that in mind.
Hey Will thanks for the information. I will keep that in mind.
Sir, can you give me full code.?
Trying to implement this code. What are the requirements? Does it need a certain version of PHP, etc? Do I ...
 
© 2008 Will McGugan.

A technoblog blog, design by Will McGugan