# HG changeset patch
# User Laman
# Date 2019-02-17 10:44:37
# Node ID 90090fca08f902ba4d88f29469c583f28ef82cfe
# Parent  7c268e382b96e468bfbf9c390a2e7e92975c4187

experiment with double Hough transform

diff --git a/exp/hough.py b/exp/hough.py
--- a/exp/hough.py
+++ b/exp/hough.py
@@ -1,7 +1,6 @@
 import sys
 sys.path.append("../src")
 
-import os
 import math
 from datetime import datetime
 import logging as log
@@ -10,9 +9,98 @@ import numpy as np
 import scipy.optimize
 import cv2 as cv
 
-from annotations import DataFile,computeBoundingBox
+from geometry import Line
 from analyzer.epoint import EPoint
 
+DEBUG=True
+
+
+class BaseHough:
+	def __init__(self,width,height):
+		self._diagLen=int(np.sqrt(height**2+width**2))+1
+		self._center=(width//2,height//2)
+		self._acc=np.zeros((180,self._diagLen),dtype=np.int32)
+
+	def update(self,x,y,weight=1):
+		""":param x: number, 0 <= x < width
+		:param y: number, 0 <= y < height"""
+		for alphaDeg in range(0,180):
+			d=self._computeDist(x,y,alphaDeg)+self._diagLen//2
+			self._acc[(alphaDeg,d)]+=weight
+
+	def extract(self,n):
+		shift=self._diagLen//2
+		peaks=sorted(list(findPeaks(self._acc)),key=lambda rc: self._acc[rc],reverse=True)
+		peaks=self._filterClose(peaks)[:n]
+		log.debug("detected peaks: %s",[(alpha,d-shift) for (alpha,d) in peaks])
+
+		img=self._createImg()
+		img=self._markPeaks(img,self._filterClose(peaks))
+		self.show(img)
+
+		res=[]
+		for (alphaDeg,d) in peaks:
+			alphaRad=alphaDeg*math.pi/180
+			baseLine=Line(alphaRad,0)
+			dd=baseLine.distanceTo(EPoint(*self._center)) # to shift d from the center to 0,0
+			res.append(Line(alphaRad, dd+d-shift))
+		log.debug("detected lines: %s",res)
+		return res
+
+	def _computeDist(self,x,y,alphaDeg):
+		"""Compute the distance of a line with the provided alphaDeg declination and passing the (x,y) point to the image center.
+		The returned distance might be negative (meaning the angle is in fact alpha+180)."""
+		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 round(d)
+
+	def _filterClose(self,peaks): # a naive implementation
+		"""Discard points with Euclidean distance on the original image lower than 10.
+		From such pairs keep only the one with a higher value in the accumulator.
+		This can delete a series of points. If a-b and b-c are close and a>b>c, only a is kept."""
+		minDist=13
+		center=EPoint(*self._center)
+		res=[]
+		for (alphaDeg,d) in peaks:
+			alphaRad=alphaDeg*math.pi/180
+			point=EPoint.fromPolar((alphaRad,d),center)
+			ctrl=True
+			for (betaDeg,e) in peaks:
+				betaRad=betaDeg*math.pi/180
+				point_=EPoint.fromPolar((betaRad,e),center)
+				if point.dist(point_)<minDist and self._acc[(alphaDeg,d)]<self._acc[(betaDeg,e)]:
+					ctrl=False
+			if ctrl: res.append((alphaDeg,d))
+		return res
+
+	def show(self,img=None):
+		if not DEBUG: return
+		if img is None: img=self._createImg()
+
+		show(img,"Hough transform accumulator")
+
+	def _createImg(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)
+
+		(h,w)=img.shape[:2]
+
+		for x in range(0,w,4): # y axis
+			img[h//2,x]=[255,255,255]
+		for y in range(0,h,4):
+			img[y,w//2]=[255,255,255]
+
+		return img
+
+	def _markPeaks(self,img,peaks):
+		colors=[[255,0,0],[255,255,0],[0,255,0],[0,255,255],[0,0,255]]
+		for (i,(alpha,d)) in enumerate(peaks[:38]):
+			cv.drawMarker(img,(d,alpha),colors[i//9],cv.MARKER_TILTED_CROSS)
+		return img
+
 
 class HoughTransform:
 	def __init__(self,img):
@@ -33,15 +121,19 @@ class HoughTransform:
 		peaks=self._filterClose(peaks)
 		peaks.sort(key=lambda rc: rc[0])
 		log.debug("detected peaks: %s",peaks)
-		(alpha,beta)=self._detectDominantAngles(peaks)
+
+		h2=BaseHough(self._diagLen,180+90)
+		for (alpha,d) in peaks:
+			h2.update(d+shift,alpha)
+			if alpha<90:
+				h2.update(shift-d,alpha+180)
+		lines=h2.extract(3)
 
 		img=self._createImg()
 		img=self._markPeaks(img,self._filterClose(allPeaks[:38]))
-		doublePeaks=peaks+[(alpha+180,-d) for (alpha,d) in peaks]
-		params=self._computeGridParams([(gamma,d+shift) for (gamma,d) in doublePeaks if alpha<=gamma<=alpha+self._angleBandwidth])
-		self._drawGridCurve(img,params,0)
-		params=self._computeGridParams([(gamma,d+shift) for (gamma,d) in doublePeaks if beta<=gamma<=beta+self._angleBandwidth])
-		self._drawGridCurve(img,params,1)
+
+		for (i,line) in enumerate(lines):
+			self.drawLine(img,line,i)
 		self.show(img)
 
 	def update(self,img,weight=1):
@@ -141,13 +233,26 @@ class HoughTransform:
 		(h,w)=img.shape[:2]
 		curve=lambda x: a*x**3+b*x**2+c*x+d
 		for x in range(0,w,3):
-			y=int(curve(x))
+			y=round(curve(x))
 			if y<0 or y>=2*h: continue
 			if y<h:	img[y,x]=color
 			else: img[y%h,-x]=color
 
+	def drawLine(self,img,line,colorKey=0):
+		colors=[[0,255,255],[255,0,255],[255,255,0]]
+		color=colors[colorKey]
+		(h,w)=img.shape[:2]
+		(a,b,c)=line.toNormal()
+		print("%",a,b,c)
+		if b==0: return
+		for x in range(1,w,3):
+			y=round((-c-a*x)/b) + (0 if b>=0 else 180)
+			if y<0 or y>=h: continue
+			img[y,x]=color
+
 
 def findPeaks(arr2d): # a naive implementation
+	"""Scan 8-neighbourhood and for each peak or top plateau yield one point. For plateaus yield the """
 	(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):
@@ -207,35 +312,3 @@ def houghLines(bwImg):
 		cv.line(colorImg,(x1,y1),(x2,y2),(0,255,0),1)
 
 	show(colorImg)
-
-
-if __name__=="__main__":
-	i=sys.argv[1]
-	annotations=DataFile("/home/laman/Projekty/python/oneEye/images/annotations.json.gz")
-	filename="{0}.jpg".format(i)
-	img=cv.imread(os.path.join("/home/laman/Projekty/python/oneEye/images/",filename))
-	(x1,y1,x2,y2)=computeBoundingBox(annotations[filename][0])
-	img=img[y1:y2, x1:x2, :]
-	# blurred=cv.GaussianBlur(img,(5,5),0)
-	# small=cv.resize(img,None,fx=0.5,fy=0.5,interpolation=cv.INTER_AREA)
-	small=img
-	clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
-	gray=cv.cvtColor(small,cv.COLOR_BGR2GRAY)
-	# gray=clahe.apply(gray)
-	show(gray)
-	edges=cv.Canny(gray,70,130)
-	show(edges)
-	edges=filterHor(edges)+filterVert(edges)+filterDiag(edges)
-	show(edges)
-
-
-	# kernel = np.ones((2,2),np.uint8)
-	# edges = cv.morphologyEx(edges, cv.MORPH_DILATE, kernel)
-	# show(edges)
-	# edges=cv.morphologyEx(edges,cv.MORPH_ERODE,kernel)
-	# show(edges)
-	colorEdges=cv.cvtColor(edges,cv.COLOR_GRAY2BGR)
-
-	# houghLines(edges)
-	h=HoughTransform(edges)
-	h.extract()