diff --git a/exp/board_detect.py b/exp/board_detect.py --- a/exp/board_detect.py +++ b/exp/board_detect.py @@ -81,6 +81,10 @@ class BoardDetector: self._rectH=0 self._rect=None + self._hough=None + self._rectiMatrix=None + self._inverseMatrix=None + def __call__(self,img,filename): # approximately detect the board (h,w)=img.shape[:2] @@ -106,14 +110,14 @@ class BoardDetector: # detect lines from edges and stones edgeImg=prepareEdgeImg(rect) - hough=HoughTransform(edgeImg) + self._hough=HoughTransform(edgeImg) stonesImg=np.zeros((self._rectH,self._rectW),np.uint8) for (point,c) in stones: cv.circle(stonesImg,(int(point.x),int(point.y)),2,255,-1) show(stonesImg,"detected stones") - hough.update(stonesImg,10) - lines=hough.extract() + self._hough.update(stonesImg,10) + lines=self._hough.extract() linesImg=np.copy(rect) for line in itertools.chain(*lines): @@ -123,18 +127,10 @@ class BoardDetector: # # rectify the image matrix=self._computeTransformationMatrix(lines[0][0],lines[0][-1],lines[1][0],lines[1][-1]) transformed=cv.warpPerspective(rect,matrix,(self._rectW,self._rectH)) + rectiLines=[[line.transform(matrix) for line in pack] for pack in lines] # determine precise board edges - intersections=[] - for p in lines[0]: - for q in lines[1]: - intersections.append(p.intersect(q)) - sack=DiagonalRansac(intersections,19) - diagonals=sack.extract(10,2000) - log.debug("diagonals candidates: %s",diagonals) - for line in diagonals: - self._drawLine(linesImg,line,[0,255,255]) - show(linesImg,"diagonals candidates") + self._detectBestGrid(rectiLines,linesImg) def _detectRough(self,img,filename): corners=self._annotations[filename][0] @@ -181,10 +177,11 @@ class BoardDetector: def _computeTransformationMatrix(self,p,q,r,s): # p || q, r || s (a,b,c,d)=Corners([p.intersect(r),p.intersect(s),q.intersect(r),q.intersect(s)]) # canonize the abcd order - a_=EPoint(b.x,min(a.y,d.y)) - b_=EPoint(b.x,max(b.y,c.y)) - c_=EPoint(c.x,max(b.y,c.y)) - d_=EPoint(c.x,min(a.y,d.y)) + pad=20 + a_=EPoint(b.x+pad,min(a.y,d.y)+pad) + b_=EPoint(b.x+pad,max(b.y,c.y)-pad) + c_=EPoint(c.x-pad,max(b.y,c.y)-pad) + d_=EPoint(c.x-pad,min(a.y,d.y)+pad) abcd=[list(point) for point in (a,b,c,d)] abcd_=[list(point) for point in (a_,b_,c_,d_)] log.debug("abcd: %s ->",(a,b,c,d)) @@ -199,8 +196,72 @@ class BoardDetector: transformed=cv.warpPerspective(rect,matrix,(self._rectW,self._rectH)) show(transformed,"rectified image") + self._rectiMatrix=matrix + self._inverseMatrix=np.linalg.inv(matrix) return matrix + def _detectBestGrid(self,lines,img): + intersections=[] + for p in lines[0]: + for q in lines[1]: + intersections.append(p.intersect(q)) + + sack=DiagonalRansac(intersections,19) + diagonals=sack.extract(10,3000) + log.debug("diagonals candidates: %s",diagonals) + for line in diagonals: + self._drawLine(img,line.transform(self._inverseMatrix),[0,255,255]) + show(img,"diagonals candidates") + + best=(0,None) + transformedImg=cv.warpPerspective(img,self._rectiMatrix,(self._rectW,self._rectH)) + + for e in diagonals: + for f in diagonals: + center=e.intersect(f) + if not center: continue + if center.x<0 or center.x>self._rectW or center.y<0 or center.y>self._rectH: continue + for line in itertools.chain(*lines): + for i in range(1,10): # 10th is useless, 11-19 are symmetrical to 1-9 + grid=self._constructGrid(e,f,line,i) + if not grid: continue + score=self._scoreGrid(grid) + if score>best[0]: + best=(score,grid) + log.debug("new best grid: %s",score) + self._showGrid(transformedImg,grid) + return best[1] + + def _constructGrid(self,e,f,line,i): + """Contruct a grid. + + :param e: (Line) one diagonal + :param f: (Line) other diagonal + :param line: (Line) one of the grid lines + :param i: (int) line's index among the grid's lines, 1<=i<=9""" + center=e.intersect(f) + p1=line.intersect(e) + p2=line.intersect(f) + a=center+9*(p1-center)/(10-i) + b=center+9*(p2-center)/(10-i) + c=2*center-a + d=2*center-b + # abort unfitting sizes + if not all(0<=point.x