Changeset - 6aace8f39e75
[Not reviewed]
default
0 2 1
Laman - 6 years ago 2019-03-04 19:29:53

detecting board diagonals with RANSAC
3 files changed with 86 insertions and 4 deletions:
0 comments (0 inline, 0 general)
exp/board_detect.py
Show inline comments
 
import sys
 

	
 
sys.path.append("../src")
 

	
 
import os
 
import math
 
import random
 
import itertools
 
import logging as log
 

	
 
import cv2 as cv
 
import numpy as np
 
import scipy.cluster
 
import scipy.ndimage
 
import scipy.signal
 

	
 
from geometry import Line
 
from ransac import DiagonalRansac
 
from annotations import DataFile,computeBoundingBox
 
from hough import show,prepareEdgeImg,HoughTransform
 
from analyzer.epoint import EPoint
 
from analyzer.corners import Corners
 

	
 
random.seed(361)
 
log.basicConfig(level=log.DEBUG,format="%(message)s")
 

	
 

	
 
def kmeans(img):
 
	arr=np.reshape(img,(-1,3)).astype(np.float)
 
	wood=[193,165,116]
 
@@ -116,24 +116,34 @@ class BoardDetector:
 
		lines=hough.extract()
 

	
 
		linesImg=np.copy(rect)
 
		for line in itertools.chain(*lines):
 
			self._drawLine(linesImg,line)
 
		show(linesImg,"detected lines")
 

	
 
		# # 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))
 

	
 
		# 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")
 

	
 
	def _detectRough(self,img,filename):
 
		corners=self._annotations[filename][0]
 
		(x1,y1,x2,y2)=computeBoundingBox(corners)
 
		log.debug("bounding box: (%s,%s) - (%s,%s)",x1,y1,x2,y2)
 
		return (x1,y1,x2,y2)
 

	
 
	def _sampleColors(self,rect):
 
		(h,w)=rect.shape[:2]
 
		minirect=rect[h//4:3*h//4, w//4:3*w//4]
 
		return kmeans(minirect)
 

	
 
@@ -182,37 +192,38 @@ class BoardDetector:
 
		matrix=cv.getPerspectiveTransform(np.float32(abcd),np.float32(abcd_))
 
		log.debug("transformation matrix: %s",matrix)
 

	
 
		rect=np.copy(self._rect)
 
		for point in (a,b,c,d):
 
			cv.drawMarker(rect,(int(point.x),int(point.y)),(0,255,255),cv.MARKER_TILTED_CROSS)
 
		show(rect)
 
		transformed=cv.warpPerspective(rect,matrix,(self._rectW,self._rectH))
 
		show(transformed,"rectified image")
 

	
 
		return matrix
 

	
 
	def _drawLine(self,img,line):
 
	def _drawLine(self,img,line,color=None):
 
		if not color: color=[0,255,0]
 
		(h,w)=img.shape[:2]
 
		corners=[EPoint(0,0),EPoint(w,0),EPoint(0,h),EPoint(w,h)] # NW NE SW SE
 
		borders=[
 
			[Line.fromPoints(corners[0],corners[1]), Line.fromPoints(corners[2],corners[3])], # N S
 
			[Line.fromPoints(corners[0],corners[2]), Line.fromPoints(corners[1],corners[3])] # W E
 
		]
 

	
 
		(a,b)=(line.intersect(borders[0][0]), line.intersect(borders[0][1]))
 
		log.debug("%s %s",line,(a,b))
 
		if not a or not b:
 
			(a,b)=(line.intersect(borders[1][0]), line.intersect(borders[1][1]))
 
			log.debug("* %s %s",line,(a,b))
 
		if any(abs(x)>10**5 for x in [*a,*b]):
 
			log.debug("ignored")
 
			return
 
		cv.line(img,(int(a.x),int(a.y)),(int(b.x),int(b.y)),[0,255,0])
 
		cv.line(img,(int(a.x),int(a.y)),(int(b.x),int(b.y)),color)
 

	
 

	
 
if __name__=="__main__":
 
	detector=BoardDetector(sys.argv[2])
 
	filepath=sys.argv[1]
 
	filename=os.path.basename(filepath)
 
	img=cv.imread(filepath)
 
	detector(img,filename)
exp/hough.py
Show inline comments
 
@@ -18,24 +18,25 @@ DEBUG=True
 
class LineBag:
 
	def __init__(self):
 
		self._lines=[]
 

	
 
	def put(self,score,alpha,beta,peaks):
 
		self._lines.append((score,alpha,beta,peaks))
 

	
 
	def pull(self,count):
 
		self._lines.sort(reverse=True)
 
		res=[]
 
		for (score,alpha,beta,peaks) in self._lines:
 
			if any(abs(alpha-gamma)<10 and abs(beta-delta)<10 for (_,gamma,delta,_) in res): continue
 
			# avoid intersecting lines
 
			if any((beta-delta)!=0 and (alpha-gamma)/(beta-delta)<0 for (_,gamma,delta,_) in res): continue
 
			res.append((score,alpha,beta,peaks))
 
			if len(res)>=count: break
 
		return res
 

	
 

	
 
class HoughTransform:
 
	"""Find line sequences with Hough transform.
 

	
 
	Uses usual image coordinates on input and output, with [0,0] in the upper left corner and [height-1,width-1] in the lower right.
 
	However, internally it uses the usual cartesian coordinates, centered at the image center. [-w/2,-h/2] in the upper left and [w/2,h/2] in the lower right."""
 
	def __init__(self,img):
 
@@ -56,25 +57,25 @@ class HoughTransform:
 
		i=0
 
		for (score,alpha,beta,peaks) in lines:
 
			log.debug("score: %s",score)
 
			log.debug("alpha, beta: %s, %s",alpha,beta)
 
			self._drawLine(img,alpha,beta,peaks,i)
 

	
 
			res.append([])
 
			keys=self._readLineKeys(alpha,beta)
 
			for k in peaks:
 
				(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)
 
			res[-1].sort(key=lambda line: line.d if line.alpha<math.pi else -line.d)
 
			i+=1
 

	
 
		self.show(img)
 
		return res
 

	
 
	def update(self,img,weight=1):
 
		start=datetime.now().timestamp()
 
		for (r,row) in enumerate(img):
 
			for (c,pix) in enumerate(row):
 
				if pix==0: continue
 
				for alphaDeg in range(0,180):
 
					d=self._computeDist(c,r,alphaDeg)+self._diagLen//2
exp/ransac.py
Show inline comments
 
new file 100644
 
import random
 

	
 
from geometry import Line
 

	
 

	
 
class LineBag:
 
	def __init__(self,capacity):
 
		self._capacity=capacity
 
		self._bag=[]
 

	
 
	def put(self,item):
 
		# early abort
 
		if self._bag and item>self._bag[-1]: return
 

	
 
		(v,p)=item
 
		# mostly filter duplicates
 
		for (i,(vi,pi)) in enumerate(self._bag):
 
			if abs(p.alpha-pi.alpha)<0.02 and abs(p.d-pi.d)<5:
 
				if v<vi: self._bag[i]=item
 
				return
 

	
 
		# add new and remove the last
 
		self._bag.append(item)
 
		self._bag.sort()
 
		if len(self._bag)>self._capacity:
 
			self._bag.pop()
 

	
 
	def pull(self,count=0):
 
		return self._bag[:count] if count else self._bag[:]
 

	
 

	
 
class Ransac:
 
	def __init__(self,points):
 
		self._points=points
 

	
 
	def extract(self,count,iterations):
 
		bag=LineBag(count)
 

	
 
		for i in range(iterations):
 
			line=self._generateLine()
 
			score=self._scoreLine(line)
 
			bag.put((score+random.random()/1000,line))
 

	
 
		return [line for (score,line) in bag.pull()]
 

	
 
	def _generateLine(self):
 
		(a,b)=random.sample(self._points,2)
 
		return Line.fromPoints(a,b)
 

	
 
	def _scoreLine(self,line):
 
		return sum(min(line.distanceTo(p),5) for p in self._points)
 

	
 

	
 
class DiagonalRansac(Ransac):
 
	def __init__(self,points,sideLen):
 
		super().__init__(points)
 
		self._sideLen=sideLen
 

	
 
	def _generateLine(self):
 
		n=len(self._points)
 
		m=self._sideLen
 
		ka=random.randrange(0,n)
 
		kb=ka
 

	
 
		while ka//m==kb//m or ka%m==kb%m:
 
			kb=random.randrange(0,n)
 
		a=self._points[ka]
 
		b=self._points[kb]
 

	
 
		return Line.fromPoints(a,b)
0 comments (0 inline, 0 general)