Changeset - 5617054647db
[Not reviewed]
default
0 3 0
Laman - 6 years ago 2019-03-11 14:39:44

grid construction and evaluation
3 files changed with 105 insertions and 19 deletions:
0 comments (0 inline, 0 general)
exp/board_detect.py
Show inline comments
 
@@ -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<self._rectW and 0<=point.y<self._rectH for point in (a,b,c,d)):
 
			return None
 
		if any(g.dist(h)<19*10 for (g,h) in [(a,b),(a,c),(a,d),(b,c),(b,d),(c,d)]):
 
			return None
 
		(a,b,c,d)=Corners([a,b,c,d])
 
		rows=[]
 
		cols=[]
 
		for j in range(19):
 
			rows.append(Line.fromPoints((a*(18-j)+b*j)/18,(d*(18-j)+c*j)/18))
 
			cols.append(Line.fromPoints((a*(18-j)+d*j)/18,(b*(18-j)+c*j)/18))
 
		return (rows,cols)
 

	
 
	def _scoreGrid(self,lines):
 
		return sum(self._hough.scoreLine(p.transform(self._inverseMatrix)) for p in itertools.chain(*lines))
 

	
 
	def _drawLine(self,img,line,color=None):
 
		if not color: color=[0,255,0]
 
		(h,w)=img.shape[:2]
 
@@ -220,6 +281,16 @@ class BoardDetector:
 
			return
 
		cv.line(img,(int(a.x),int(a.y)),(int(b.x),int(b.y)),color)
 

	
 
	def _showGrid(self,img,lines):
 
		img=np.copy(img)
 
		(rows,cols)=lines
 
		for row in rows:
 
			for col in cols:
 
				point=row.intersect(col)
 
				xy=(int(point.x),int(point.y))
 
				cv.circle(img,xy,4,[0,0,255],-1)
 
		show(img,"grid candidate")
 

	
 

	
 
if __name__=="__main__":
 
	detector=BoardDetector(sys.argv[2])
exp/hough.py
Show inline comments
 
@@ -66,7 +66,7 @@ class HoughTransform:
 
				(alphaDeg,d)=keys[k]
 
				line=Line(alphaDeg*math.pi/180,d-self._diagLen//2)
 
				res[-1].append(self._transformOutput(line))
 
			res[-1].sort(key=lambda line: line.d if line.alpha<math.pi else -line.d)
 
			res[-1].sort(key=lambda line: line.d)
 
			i+=1
 

	
 
		self.show(img)
 
@@ -82,6 +82,13 @@ class HoughTransform:
 
					self._acc[(alphaDeg,d)]+=weight
 
		log.debug("Hough updated in %s s",round(datetime.now().timestamp()-start,3))
 

	
 
	def scoreLine(self,line):
 
		transformed=self._transformInput(line)
 
		alphaDeg=round(transformed.alpha*180/math.pi)%180
 
		d=round(transformed.d+self._diagLen//2)
 
		if not 0<=d<self._diagLen: return 0
 
		return self._acc[(alphaDeg,d)]
 

	
 
	def show(self,img=None):
 
		if img is None: img=self._createImg()
 
		show(img,"Hough transform accumulator")
 
@@ -114,6 +121,15 @@ class HoughTransform:
 
			res.append((k,i))
 
		return res
 

	
 
	def _transformInput(self,line):
 
		reflectedLine=Line(math.pi*2-line.alpha,line.d)
 
		(x,y)=self._center
 
		basis=EPoint(x,-y)
 
		shiftedLine=reflectedLine.shiftBasis(basis)
 
		if shiftedLine.alpha>=math.pi:
 
			shiftedLine=Line(shiftedLine.alpha-math.pi,-shiftedLine.d)
 
		return shiftedLine
 

	
 
	def _transformOutput(self,line):
 
		(x,y)=self._center
 
		basis=EPoint(-x,y)
src/analyzer/corners.py
Show inline comments
 
@@ -88,7 +88,6 @@ class Corners:
 

	
 
		self._corners= self._corners[kIndex:] + self._corners[:kIndex] # rotate the upper left corner to the first place
 

	
 
		log.debug(self._corners)
 
		self._is_canon=True # success
 
		return True
 

	
0 comments (0 inline, 0 general)