Original Question
So I've been reading up alot on how to do a Perspective transform on an image in pure Python but I can't get it to work.
The transform itself is pretty simple, it's just a small equation, but I'm having trouble with the function for creating the coefficients from certain anchor points in the image. I've gotten a working code that completes without errors with the help of the excellent numpy-based answer from another post, but my issue is that I'm trying to do this in pure-Python. The coefficients turn out redicilously low, and nothing pops up on my resulting image.
One issue could be in the porting of the numpy functions like dotmatrix, multiplication, and inverse matrix that I'm trying to do in pure Python functions. (I do get a perspective transformed image when I manually input each of the coefficients so I know the coefficient to rendering part should be working). So any help with figuring out what might be wrong with how I calculate the coefficients is greatly appreciated :)
Update
Alright, quick update, it turns out that the numpy code suddenly works now and produces what looks like a perspectived image (originally from the post I linked to earlier), so that's at least a good sign. Here is the exact numpy code I used, and thus the template of steps to be reproduced in pure Python.
import numpy
matrix = []
print pa,pb
for p1, p2 in zip(pa, pb):
matrix.append([p1[0], p1[1], 1, 0, 0, 0, -p2[0]*p1[0], -p2[0]*p1[1]])
matrix.append([0, 0, 0, p1[0], p1[1], 1, -p2[1]*p1[0], -p2[1]*p1[1]])
A = numpy.matrix(matrix, dtype=numpy.float)
B = numpy.array(pb).reshape(8)
print "ab",A,B
res = numpy.dot(numpy.linalg.inv(A.T * A) * A.T, B)
print "res",numpy.array(res).reshape(8)
And here is the pure Python version that currently attempts to reproduce the steps but fails:
def tilt(self, oldplane, newplane):
"""
perspective transform.
oldplane is a list of four old xy coordinate pairs
that will move to the four points in the newplane
"""
#first find the transform coefficients, thanks to https://stackoverflow.com/questions/14177744/how-does-perspective-transformation-work-in-pil
pb,pa = oldplane,newplane
grid = []
for p1,p2 in zip(pa, pb):
grid.append([p1[0], p1[1], 1, 0, 0, 0, -p2[0]*p1[0], -p2[0]*p1[1]])
grid.append([0, 0, 0, p1[0], p1[1], 1, -p2[1]*p1[0], -p2[1]*p1[1]])
def transpose(listoflists):
return [list(each) for each in zip(*listoflists)]
def flatten(listoflists):
return [xory for xy in listoflists for xory in xy]
def sumproduct(listA,multis):
"aka, dot multiplication"
outgrid = []
for y,(row,multi) in enumerate(zip(listA,multis)):
rowsum = 0
for x,nr in enumerate(row):
rowsum += nr*multi
outgrid.append(rowsum)
return outgrid
def gridmultiply(grid1,grid2):
"aka, matrix*matrix"
outgrid = []
for y in xrange(len(grid1)):
newrow = []
for x in xrange(len(grid2)):
value = grid1[y][x] * grid2[y][x]
newrow.append(value)
outgrid.append(newrow)
return outgrid
def gridinverse(grid):
outgrid = []
pos_deriv = 1
neg_deriv = 1
for y in xrange(len(grid)):
horizline = []
for x in xrange(len(grid[0])):
invx=len(grid[0])-1-x
invy=len(grid)-1-y
nr = grid[y][x]
pos_deriv += pos_deriv*nr
invnr = grid[invy][invx]*-1
horizline.append(invnr)
neg_deriv += neg_deriv*invnr
outgrid.append(horizline)
derivative = 1/float(pos_deriv-neg_deriv)
print "deriv",derivative
outgrid = gridmultiply(outgrid,[[derivative for _ in xrange(len(outgrid[0]))] for _ in xrange(len(outgrid))])
return outgrid
A = grid
B = flatten(pb)
res = sumproduct(gridinverse(gridmultiply(gridmultiply(transpose(A),A),transpose(A))), B)
transcoeff = res
print transcoeff
#then calculate new coords, thanks to http://math.stackexchange.com/questions/413860/is-perspective-transform-affine-if-it-is-why-its-impossible-to-perspective-a"
k = 1
a,b,c,d,e,f,g,h = transcoeff
outimg = Image().new(self.width,self.height)
for y in xrange(len(self.imagegrid)):
for x in xrange(len(self.imagegrid[0])):
color = self.get(x,y)
newx = int(round((a*x+b*y+c)/float(g*x+h*y+k)))
newy = int(round((d*x+e*y+f)/float(g*x+h*y+k)))
try:
outimg.put(newx,newy,color)
#print x,y,newx,newy
except IndexError:
#out of bounds
pass
return outimg