Changeset - ffa9f7f12374
[Not reviewed]
default
0 4 0
Laman - 6 years ago 2019-02-01 17:52:05

experimenting with own Hough transform
4 files changed with 131 insertions and 50 deletions:
0 comments (0 inline, 0 general)
exp/board_detect.py
Show inline comments
 
@@ -16,7 +16,7 @@ import scipy.signal
 
from geometry import Line, point2lineDistance
 
from polar_hough import PolarHough
 
from annotations import DataFile,computeBoundingBox
 
from hough import show
 
from hough import show,prepareEdgeImg,HoughTransform
 
from analyzer.epoint import EPoint
 
from analyzer.corners import Corners
 

	
 
@@ -64,13 +64,13 @@ def filterStones(contours,bwImg,stoneDim
 
			cv.drawMarker(contourImg,tuple(map(int,center)),(255,0,0),cv.MARKER_CROSS)
 
	log.debug("accepted: %s",len(res))
 
	log.debug("rejected: %s",len(contours)-len(res))
 
	show(contourImg)
 
	show(contourImg,"accepted and rejected stones")
 
	return res
 

	
 

	
 
def groupLines(points,minCount,tolerance):
 
	random.shuffle(points)
 
	sample=points[:57]
 
	sample=points[:]
 
	for (i,a) in enumerate(sample):
 
		for (j,b) in enumerate(sample):
 
			if j<=i: continue
 
@@ -110,23 +110,36 @@ class BoardDetector:
 
		# detect black and white stones
 
		stones=self._detectStones(quantized,colors)
 

	
 
		# detect lines passing through the stones
 
		lines=self._constructLines(stones)
 
		# detect lines from edges and stones
 
		edgeImg=prepareEdgeImg(rect)
 
		hough=HoughTransform(edgeImg)
 
		hough.extract()
 
		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)
 
		# cv.drawContours(stonesImg,[c for (point,c) in stones],-1,255,-1)
 
		show(stonesImg,"detected stones")
 
		# hough.update(stonesImg,5)
 
		hough=HoughTransform(stonesImg)
 
		hough.extract()
 

	
 
		# detect vanishing points of the lines
 
		imgCenter=EPoint(w//2-x1, h//2-y1)
 
		vanish=self._detectVanishingPoints(lines,imgCenter)
 
		(a,b,c,d)=(p-EPoint(x1,y1) for p in self._annotations[filename][0])
 
		(p,q,r,s)=(Line(a,b),Line(b,c),Line(c,d),Line(d,a))
 
		v1=p.intersect(r)
 
		v2=q.intersect(s)
 
		log.debug("true vanishing points: %s ~ %s, %s ~ %s",v1,v1.toPolar(imgCenter),v2,v2.toPolar(imgCenter))
 

	
 
		# rectify the image
 
		matrix=self._computeTransformationMatrix(vanish,lines)
 
		transformed=cv.warpPerspective(rect,matrix,(self._rectW,self._rectH))
 

	
 
		# determine precise board edges
 
		# # detect lines passing through the stones
 
		# lines=self._constructLines(stones)
 
		#
 
		# # detect vanishing points of the lines
 
		# imgCenter=EPoint(w//2-x1, h//2-y1)
 
		# (a,b,c,d)=(p-EPoint(x1,y1) for p in self._annotations[filename][0])
 
		# (p,q,r,s)=(Line(a,b),Line(b,c),Line(c,d),Line(d,a))
 
		# v1=p.intersect(r)
 
		# v2=q.intersect(s)
 
		# log.debug("true vanishing points: %s ~ %s, %s ~ %s",v1,v1.toPolar(imgCenter),v2,v2.toPolar(imgCenter))
 
		# vanish=self._detectVanishingPoints(lines,imgCenter,(v1.toPolar(imgCenter),v2.toPolar(imgCenter)))
 
		#
 
		# # rectify the image
 
		# matrix=self._computeTransformationMatrix(vanish,lines)
 
		# transformed=cv.warpPerspective(rect,matrix,(self._rectW,self._rectH))
 
		#
 
		# # determine precise board edges
 

	
 
	def _detectRough(self,img,filename):
 
		corners=self._annotations[filename][0]
 
@@ -158,20 +171,22 @@ class BoardDetector:
 
		maskB=cv.erode(maskB,kernel,iterations=3)
 
		# distTransform = cv.distanceTransform(maskB,cv.DIST_L2,5)
 
		# maskB=cv.inRange(distTransform,6,10)
 
		show(maskB)
 
		show(maskB,"black areas")
 
		maskW=cv.inRange(quantized,colors[1]-unit,colors[1]+unit)
 
		maskW=cv.morphologyEx(maskW,cv.MORPH_OPEN,kernel,iterations=1)
 
		maskW=cv.erode(maskW,kernel,iterations=2)
 
		show(maskW)
 
		show(maskW,"white areas")
 
		stones=cv.bitwise_or(maskB,maskW)
 
		show(stones)
 
		show(stones,"black and white areas")
 
		return stones
 

	
 
	def _constructLines(self,stoneLocs):
 
		lineDict=dict()
 
		minCount=min(max(math.sqrt(len(stoneLocs))-4,3),7)
 
		# minCount=min(max(math.sqrt(len(stoneLocs))-4,3),7)
 
		minCount=6
 
		log.debug("min count: %s",minCount)
 
		for line in groupLines([point for (point,contour) in stoneLocs],minCount,2):
 
		points=[point for (point,contour) in stoneLocs]
 
		for line in groupLines(points,minCount,2):
 
			key=line.getSortedPoints()
 
			if key in lineDict: # we already have a line with the same incident points
 
				continue
 
@@ -193,6 +208,7 @@ class BoardDetector:
 
		cv.drawContours(linesImg,[c for (point,c) in stoneLocs],-1,(255,255,255),-1)
 
		for (p,c) in stoneLocs:
 
			cv.drawMarker(linesImg,(int(p.x),int(p.y)),(255,0,0),cv.MARKER_CROSS)
 
		self._printLines(lines,points,linesImg)
 
		for line in lines:
 
			points=line.getSortedPoints()
 
			(xa,ya)=points[0]
 
@@ -202,15 +218,28 @@ class BoardDetector:
 

	
 
		return lines
 

	
 
	def _detectVanishingPoints(self,lines,imgCenter):
 
	def _printLines(self,lines,allPoints,img):
 
		for (i,line) in enumerate(lines):
 
			img_=np.copy(img)
 
			points=list(line.getSortedPoints())
 
			(a,b)=max(((a,b) for a in points for b in points if a<b),key=lambda ab: ab[0].dist(ab[1]))
 
			(xa,ya)=a
 
			(xb,yb)=b
 
			points.sort(key=lambda p: a.dist(p))
 
			cv.line(img_,(int(xa),int(ya)),(int(xb),int(yb)),(255,255,0),1)
 
			cv.imwrite("/tmp/{0}.png".format(i),img_)
 
			pointDists=",".join(str(round(p1.dist(p2),3)) for (p1,p2) in zip(points[:-1],points[1:]))
 
			log.debug("\t".join(map(str,[i,line,line.score(allPoints),pointDists])))
 

	
 
	def _detectVanishingPoints(self,lines,imgCenter,trueVs):
 
		polarHough=PolarHough(math.pi/180,10)
 
		for (i,ab) in enumerate(lines):
 
			for cd in lines[i+1:]:
 
				point=ab.intersect(cd)
 
				if 0<=point.x<=self._rectW and 0<=point.y<=self._rectH: continue
 
				log.debug("%s -> %s",point,point.toPolar(imgCenter))
 
				# log.debug("%s -> %s",point,point.toPolar(imgCenter))
 
				polarHough.put(point.toPolar(imgCenter))
 
		vanish=[EPoint.fromPolar(p,imgCenter) for p in polarHough.extract(2)]
 
		vanish=[EPoint.fromPolar(p,imgCenter) for p in polarHough.extract(2,trueVs)]
 
		log.debug(vanish)
 
		return vanish
 

	
exp/geometry.py
Show inline comments
 
@@ -37,6 +37,13 @@ class Line():
 
		if a is None or b is None: return None
 
		return Line(a,b)
 

	
 
	def score(self,points):
 
		score=len(self.points)
 
		for a in self.points:
 
			closest=sorted(points,key=lambda b: a.dist(b))[:4]
 
			score+=sum(0.01 for b in closest if b in self.points)
 
		return score
 

	
 
	def __str__(self): return "({0},{1})".format(self.a,self.b)
 
	def __repr__(self): return "Line({0},{1})".format(repr(self.a),repr(self.b))
 

	
exp/hough.py
Show inline comments
 
import os
 
import sys
 
import math
 
from datetime import datetime
 
import logging as log
 

	
 
import numpy as np
 
import cv2 as cv
 
@@ -7,6 +10,49 @@ import cv2 as cv
 
from annotations import DataFile,computeBoundingBox
 

	
 

	
 
class HoughTransform:
 
	def __init__(self,img):
 
		(h,w)=img.shape[:2]
 
		diagLen=np.sqrt(h**2+w**2)
 
		self._center=(w//2,h//2)
 
		self._acc=np.zeros((360,int(diagLen//2)+1),dtype=np.int32)
 

	
 
		self.update(img)
 

	
 
	def extract(self):
 
		maxVal=self._acc.max()
 
		arr=np.expand_dims(np.uint8(255*self._acc//maxVal),axis=2)
 
		img=np.concatenate((arr,arr,arr),axis=2)
 
		log.debug(sorted(list(findPeaks(self._acc)),key=lambda rc: self._acc[rc],reverse=True)[:2*19])
 
		show(img,"Hough transform accumulator")
 

	
 
	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,360):
 
					d=self._computeDist(c,r,alphaDeg)
 
					if d>=0: self._acc[(alphaDeg,d)]+=weight
 
		log.debug("Hough updated in %s s",round(datetime.now().timestamp()-start,3))
 

	
 
	def _computeDist(self,x,y,alphaDeg):
 
		alphaRad=alphaDeg*math.pi/180
 
		(x0,y0)=self._center
 
		(dx,dy)=(x-x0,y-y0)
 
		d=dx*math.cos(alphaRad)+dy*math.sin(alphaRad)
 
		return int(d)
 

	
 

	
 
def findPeaks(arr2d): # naive implementation
 
	(h,w)=arr2d.shape
 
	neighbours=[(-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1)]
 
	for r in range(h):
 
		for c in range(w):
 
			if all(r+dr<0 or r+dr>=h or c+dc<0 or c+dc>=w or arr2d[r,c]>arr2d[r+dr,c+dc] or (i<4 and arr2d[r,c]>=arr2d[r+dr,c+dc]) for (i,(dr,dc)) in enumerate(neighbours)):
 
				yield (r,c)
 

	
 

	
 
def show(img,filename="x"):
 
	cv.imshow(filename,img)
 
	cv.waitKey(0)
 
@@ -14,7 +60,6 @@ def show(img,filename="x"):
 

	
 

	
 
def filterVert(edges):
 
	# !! cv.morphologyEx()
 
	kernel = np.array([[1,0,1],[1,0,1],[1,0,1]],np.uint8)
 
	edges = cv.erode(edges,kernel)
 
	kernel=np.array([[0,1,0],[0,1,0],[0,1,0]],np.uint8)
 
@@ -41,6 +86,15 @@ def filterDiag(edges):
 

	
 
	return edges1+edges2
 

	
 
def prepareEdgeImg(img):
 
	gray=cv.cvtColor(img,cv.COLOR_BGR2GRAY)
 
	show(gray,"greyscale image")
 
	edges=cv.Canny(gray,70,130)
 
	show(edges,"Canny edge detector")
 
	edges=filterHor(edges)+filterVert(edges)+filterDiag(edges)
 
	show(edges,"kernel filtered edges")
 
	return edges
 

	
 
def houghLines(bwImg):
 
	colorImg=cv.cvtColor(bwImg,cv.COLOR_GRAY2BGR)
 
	lines = cv.HoughLinesP(bwImg,1,np.pi/180,10,minLineLength=10,maxLineGap=40)
 
@@ -78,20 +132,7 @@ if __name__=="__main__":
 
	# edges=cv.morphologyEx(edges,cv.MORPH_ERODE,kernel)
 
	# show(edges)
 
	colorEdges=cv.cvtColor(edges,cv.COLOR_GRAY2BGR)
 
	# show(blurred)
 
	# show(small)
 

	
 
	# lines = cv.HoughLines(edges,1,np.pi/180,200)
 
	# if lines is None: lines=[]
 
	# for line in lines:
 
	# 	rho,theta = line[0]
 
	# 	a = np.cos(theta)
 
	# 	b = np.sin(theta)
 
	# 	x0 = a*rho
 
	# 	y0 = b*rho
 
	# 	x1 = int(x0 + 1000*(-b))
 
	# 	y1 = int(y0 + 1000*(a))
 
	# 	x2 = int(x0 - 1000*(-b))
 
	# 	y2 = int(y0 - 1000*(a))
 
	# 	cv.line(colorEdges,(x1,y1),(x2,y2),(0,0,255),1)
 
	houghLines(edges)
 
	# houghLines(edges)
 
	h=HoughTransform(edges)
 
	h.extract()
exp/polar_hough.py
Show inline comments
 
@@ -21,9 +21,9 @@ class PolarHough:
 
		k=int(item[0]//self._anglePrecision)
 
		self._acc[k].append(item)
 

	
 
	def extract(self,count):
 
	def extract(self,count,trueVs):
 
		vanishingPoints=[]
 
		angles=self._extractAngles(count)
 
		angles=self._extractAngles(count,tuple(v[0] for v in trueVs))
 
		angles=[alpha for (alpha,prominence) in angles]
 
		bins=self._mapAngles(angles)
 
		for (alpha,bin) in zip(angles,bins):
 
@@ -31,12 +31,16 @@ class PolarHough:
 
			vanishingPoints.append((alpha,length))
 
		return vanishingPoints
 

	
 
	def _extractAngles(self,k):
 
		lens=np.array(list(map(len,self._acc)))
 
		log.debug(lens)
 
	def _extractAngles(self,k,trueAngles):
 
		lens=np.array([0]+list(map(len,self._acc))+[0])
 
		marked=np.copy(lens[1:-1])
 
		for alpha in trueAngles:
 
			key=int(alpha/self._anglePrecision)
 
			marked[key]=-marked[key]-1
 
		log.debug(marked)
 
		(peakKeys,info)=scipy.signal.find_peaks(lens,prominence=0)
 
		res=sorted(zip(info["prominences"],peakKeys),reverse=True)[:k]
 
		res=[(key*self._anglePrecision,prominence) for (prominence,key) in res]
 
		res=[((key-1)*self._anglePrecision,prominence) for (prominence,key) in res]
 
		log.debug("(angle, prominence): %s ... %s",res,[alpha/self._anglePrecision for (alpha,_) in res])
 
		return res
 

	
0 comments (0 inline, 0 general)