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.

You could also use the scipy.ndimage.geometric_transform function, which will interpolate the points in the transformation, for a fuller non pixellated picture, I made a gist here,

ReplyDeleteWhich gives the following result.

Hi Alemi, thanks for your interest.

ReplyDeleteIn your gist you have lost a portion of the result, you should consider to use the parameter output_shape of the ndimage.geometric_transform.

By the way, could I post your code here?

Ah, nice catch. Sure you can post the code, it's mostly yours. ;-)

ReplyDeleteLove the blog by the way, keep it up.

There's an error in computing the b coefficient of the transformation. The second column should contain components of zp, not wa.

ReplyDelete