# Making tileable images with Python

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)
```
Use Markdown for formatting
```*Italic* **Bold** `inline code`

> This is a quote
>

```python
import this
```
```
your comment will be previewed here
Pauli Virtanen
Smells like a job for Numpy (http://www.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)
```
Will McGugan
Pauli, Nice! I think it is a job for Numpy.
Pauli Virtanen
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)
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)
```