2

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
Community
  • 1
  • 1
Karim Bahgat
  • 2,781
  • 3
  • 21
  • 27
  • 1) Why would you do this without numpy? 2) Have you got a working numpy version of this code? 3) If so have you tried writing some tests for your functions `sumproduct`, `flatten`, etc? – YXD Apr 02 '14 at 14:11
  • 1) I know it's a huge detour, but it's for a pure-Python experiment I'm writing which I might add to the pure Python imaging library "Pymaging", and it's fun! 2) I have a Numpy version of the code which I adapted from the forum post I linked to, but even that is resulting in an error in the linalg.inv function (because it received a single array instead of a matrix) but not sure which part I can change to fix it. 3) I've done some sporadic prints to compare in/outputs, but I thought I would ask here first before I went completely ninja into unknown territory :P – Karim Bahgat Apr 02 '14 at 15:29
  • 1) Fair enough 2) You should get this part working first 3) Then get your pure-Python functions working. As it is you're porting broken code while adding new untested code and it'll be very difficult to isolate the problem. I recommend you start by posting your attempt to call `find_coeffs` as defined in the other question and people can help fix that issue. – YXD Apr 02 '14 at 15:41
  • @MrE , I got the numpy calculation of the coefficients working, see the updated question. So now it just remains to recreate the various numpy matrix operations in Python, thanks in advance to anyone who knows. – Karim Bahgat Apr 05 '14 at 17:37

0 Answers0