diff --git a/src/epoint.py b/src/epoint.py --- a/src/epoint.py +++ b/src/epoint.py @@ -6,6 +6,15 @@ class EPoint: def __init__(self,x,y): self.x=x self.y=y + + def fromTuple(tup): return EPoint(tup[0],tup[1]) + + def fromProjective(point): + if point.item(0)==0: return None + return EPoint(point.item(1)/point.item(0),point.item(2)/point.item(0)) + + def toProjective(self): + return (1,self.x,self.y) def dist(self,a): return math.sqrt((self.x-a.x)**2+(self.y-a.y)**2) @@ -13,8 +22,48 @@ class EPoint: def __add__(self,a): return EPoint(self.x+a.x,self.y+a.y) + def __sub__(self,a): + return EPoint(self.x-a.x,self.y-a.y) + + def __mul__(self,k): + return EPoint(self.x*k,self.y*k) + + def __rmul__(self,k): + return self*k + def __truediv__(self,k): return EPoint(self.x/k,self.y/k) - def __str__(self): return "({0},{1})".format(self.x,self.y) - def __repr__(self): return "EPoint({0},{1})".format(self.x,self.y) + def __floordiv__(self,k): + return EPoint(self.x//k,self.y//k) + + def __iadd__(self,a): + self.x+=a.x + self.y+=a.y + return self + + def __isub__(self,a): + self.x-=a.x + self.y-=a.y + return self + + def __imul__(self,k): + self.x*=k + self.y*=k + return self + + def __itruediv__(self,k): + self.x/=k + self.y/=k + return self + + def __ifloordiv__(self,k): + self.x//=k + self.y//=k + return self + + def __neg__(self): + return EPoint(-self.x,-self.y) + + def __str__(self): return "({0},{1})".format(round(self.x,3),round(self.y,3)) + def __repr__(self): return "EPoint({0},{1})".format(round(self.x,3),round(self.y,3)) diff --git a/src/grid.py b/src/grid.py --- a/src/grid.py +++ b/src/grid.py @@ -1,74 +1,74 @@ import numpy +from epoint import * + -## Multiplicates the vector as to set the first nonzero coordinate to 1. -def canonize(v): - if v.item(0)!=0: factor=v.item(0) - elif v.item(1)!=0: factor=v.item(1) - elif v.item(2)!=0: factor=v.item(2) - else: factor=1 - return v/factor - return [x/factor for x in v] +## Projective transformation of a point with a matrix A. +# +# Takes a point as a horizontal vector and multiplies it transposed with A from left. +# +# @return transformed point as a numpy.array +def transformPoint(point,A): + return (A*numpy.matrix(point).transpose()).getA1() -def transformPoint(point,A): - # print('#68',numpy.asarray(([1]+point)*A)) - x=canonize((A*numpy.matrix([1]+point).transpose()).getA1()) - return x[1:] class Grid: - + ## Creates a Grid from the provided Corners object. + # + # Finds the vanishing points of the board lines (corner points define perspectively transformed parallel lines). The vanishing points define the image horizon. + # + # The horizon can be used to construct a matrix for affine rectification of the image (restoring parallel lines parallelism). We transform the corner points by this matrix, + # interpolate them to get proper intersections' coordinates and then transform these back to get their placement at the original image. + # + # The result is stored in grid.intersections, a boardSize*boardSize list with [row][column] coordinates. + # + # @param corners a properly initialized Corners object. !! Needs a check for the proper initialization. def __init__(self,corners): - # ab - # cd - a,b,c,d=corners + # ad + # bc + a,b,c,d=[c.toProjective() for c in corners.corners] p1=numpy.cross(a,b) p2=numpy.cross(c,d) vanish1=numpy.cross(p1,p2) - - print('#16',p1,p2,vanish1) - - p3=numpy.cross(a,c) - p4=numpy.cross(b,d) - vanish2=numpy.cross(p3,p4) + # !! 32 bit int can overflow. keeping it reasonably small. might want to use a cleaner solution + vanish1=EPoint.fromProjective(vanish1).toProjective() - print('#32',p3,p4,vanish2) - - horizon=canonize(numpy.cross(vanish1,vanish2)) + p3=numpy.cross(a,d) + p4=numpy.cross(b,c) + vanish2=numpy.cross(p3,p4) + vanish2=EPoint.fromProjective(vanish2).toProjective() - # horizon.x+=10 - # horizon[1]+=10 - - print('#48',horizon) + horizon=numpy.cross(vanish1,vanish2) + + horizon=EPoint.fromProjective(horizon).toProjective() rectiMatrix=numpy.matrix([horizon,[0,1,0],[0,0,1]]) rectiMatrixInv=numpy.linalg.inv(rectiMatrix) - print('#64',rectiMatrixInv) - print('#72',transformPoint([0,0],rectiMatrixInv)) - self.intersections=[[[c,r] for c in range(3)] for r in range(3)] - self.intersections=[[transformPoint(point,rectiMatrixInv) for point in line] for line in self.intersections] - - - # b1=canonize(numpy.cross(horizon,p1)) - # b2=canonize(numpy.cross(horizon,p2)) - # b3=canonize(numpy.cross(horizon,p3)) - # b4=canonize(numpy.cross(horizon,p4)) - - # print('#64',b1,b2,b3,b4) + affineCorners=[EPoint.fromProjective(transformPoint(x,rectiMatrix)) for x in (a,b,c,d)] + x=[affineCorners[0]-affineCorners[3],affineCorners[1]-affineCorners[2],affineCorners[0]-affineCorners[1],affineCorners[3]-affineCorners[2]] - # self.intersections=[] - # boardSize=4 - # for r in range(boardSize): - # self.intersections.append([None]*boardSize) - # rowLine=numpy.cross(((b1*r+b2*(boardSize-1-r)) / (boardSize-1)), vanish1) - # print('#80',rowLine) - # for c in range(boardSize): - # colLine=numpy.cross(((b3*c+b4*(boardSize-1-c)) / (boardSize-1)), vanish2) - # print('#88',colLine) - # self.intersections[r][c]=numpy.cross(rowLine,colLine) - -# x=Grid([Vector3(1,0,10),Vector3(1,10,10),Vector3(1,0,0),Vector3(1,10,0)]) -x=Grid([[1,0,10],[1,7,7],[1,0,0],[1,10,0]]) -for line in x.intersections: - print('#96',line) + self.intersections=[] + boardSize=19 + for r in range(boardSize): + self.intersections.append([None]*boardSize) + rowStart=(affineCorners[0]*(boardSize-1-r)+affineCorners[1]*r) / (boardSize-1) + rowEnd=(affineCorners[3]*(boardSize-1-r)+affineCorners[2]*r) / (boardSize-1) + + for c in range(boardSize): + affineIntersection=(rowStart*(boardSize-1-c)+rowEnd*c) / (boardSize-1) + self.intersections[r][c]=EPoint.fromProjective(transformPoint(affineIntersection.toProjective(),rectiMatrixInv)) + + + +# from corners import Corners +# corn=Corners() + +# corn.add(106,86) +# corn.add(57,321) +# corn.add(416,320) +# corn.add(365,86) +# corn.add(365,88) + +# x=Grid(corn) diff --git a/src/gui.py b/src/gui.py --- a/src/gui.py +++ b/src/gui.py @@ -20,7 +20,8 @@ class Application(tk.Frame): # a captured frame with overlay graphics self.imgView=tk.Canvas(self) self.imgView.configure(width=480,height=360) - imgOrig=PIL.Image.open("../images/1.jpg") + imgOrig=PIL.Image.open("../images/7.jpg") + self.img=ImageTk.PhotoImage(imgOrig.resize((int(self.imgView['width']),int(self.imgView['height'])),resample=PIL.Image.BILINEAR)) self.imgView.bind('<1>',lambda e: self.addCorner(e.x,e.y)) @@ -46,6 +47,16 @@ class Application(tk.Frame): for corner in self.corners.corners: self.markPoint(corner.x,corner.y) + if self.boardGrid!=None: + for r in range(19): + a=self.boardGrid.intersections[r][0] + b=self.boardGrid.intersections[r][-1] + self.imgView.create_line(a.x,a.y,b.x,b.y,fill='#00ff00') + for c in range(19): + a=self.boardGrid.intersections[0][c] + b=self.boardGrid.intersections[-1][c] + self.imgView.create_line(a.x,a.y,b.x,b.y,fill='#00ff00') + self.imgView.grid() ## Marks a point at the image with a green cross. Used for corners.