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 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)]

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 Imageimport numpy as npdef 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 Imageimport numpy as npimport scipy.ndimagedef 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)`

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 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
2015
• 2 posts
• 1 post
• 4 posts
• 2 posts