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)
 
@@ -122,12 +122,22 @@ 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))
 

	
 
		# 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)
 
@@ -188,13 +198,14 @@ class BoardDetector:
 
		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
 
		]
 
@@ -204,13 +215,13 @@ class BoardDetector:
 
		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)
exp/hough.py
Show inline comments
 
@@ -24,12 +24,13 @@ class LineBag:
 

	
 
	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
 

	
 

	
 
@@ -62,13 +63,13 @@ class HoughTransform:
 
			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):
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)