The Moebius transformation is defined as
where z is a complex variable different than -d/c. And a, b, c, d are complex numbers.
In this post we will see how to apply the Moebius transform to an image.
Given a set of three distinct points on the complex plane z1, z2, z3 and a second set of distinct points w1, w2, w3, there exists precisely one Moebius transformation f(z) which maps the zs to the ws. So, in the first step we have to determine f(z) from the given sets of points. We will use the
explicit determinant formula to compute the coefficients:
from pylab import *
from numpy import *
zp=[157+148j, 78+149j, 54+143j]; # (zs) the complex point zp[i]
wa=[147+143j, 78+140j, 54+143j]; # (ws) will be in wa[i]
# transformation parameters
a = linalg.det([[zp[0]*wa[0], wa[0], 1],
[zp[1]*wa[1], wa[1], 1],
[zp[2]*wa[2], wa[2], 1]]);
b = linalg.det([[zp[0]*wa[0], wa[0], wa[0]],
[zp[1]*wa[1], wa[1], wa[1]],
[zp[2]*wa[2], wa[2], wa[2]]]);
c = linalg.det([[zp[0], wa[0], 1],
[zp[1], wa[1], 1],
[zp[2], wa[2], 1]]);
d = linalg.det([[zp[0]*wa[0], zp[0], 1],
[zp[1]*wa[1], zp[1], 1],
[zp[2]*wa[2], zp[2], 1]]);
Now we can apply the transformation to the pixel coordinates.
img = fliplr(imread('mondrian.jpg')) # load an image
r = ones((500,500,3),dtype=uint8)*255 # empty-white image
for i in range(img.shape[0]):
for j in range(img.shape[1]):
z = complex(i,j)
# transformation applied to the pixels coordinates
w = (a*z+b)/(c*z+d)
r[int(real(w)),int(imag(w)),:] = img[i,j,:] # copy of the pixel
subplot(1,2,1)
title('Original Mondrian')
imshow(img)
subplot(1,2,2)
title('Mondrian after the transformation')
imshow(roll(r,120,axis=1))
show()
And this is the result.
This is another image obtained changing the vectors zp and wa.
As we can see, the result of the transformation has some empty areas that we should fill using interpolation. So let's look to another way to implement a geometric transformation on an image. The following code uses the scipy.ndimage.geometric_transform to implement the inverse Moebius transform:
from scipy.ndimage import geometric_transform
def shift_func(coords):
""" Define the moebius transformation, though backwards """
#turn the first two coordinates into an imaginary number
z = coords[0] + 1j*coords[1]
w = (d*z-b)/(-c*z+a) #the inverse mobius transform
#take the color along for the ride
return real(w),imag(w),coords[2]
r = geometric_transform(img,shift_func,cval=255,output_shape=(450,350,3))
It gives the following result:
We can see that the geometric_transform provides the pixel interpolation automatically.