diff --git a/exp/hough.py b/exp/hough.py --- a/exp/hough.py +++ b/exp/hough.py @@ -7,100 +7,31 @@ import logging as log import numpy as np import scipy.optimize +import scipy.signal import cv2 as cv -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]) +class LineBag: + def __init__(self): + self._lines=[] - 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 put(self,score,alpha,beta): + self._lines.append((score,alpha,beta)) - 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) + def pull(self,count): + self._lines.sort(reverse=True) 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_)=count: break 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): @@ -114,26 +45,13 @@ class HoughTransform: self.update(img) def extract(self): - shift=self._diagLen//2 - allPeaks=sorted(list(findPeaks(self._acc)),key=lambda rc: self._acc[rc],reverse=True) - peaks=allPeaks[:38] - peaks=[(alpha,d-shift) for (alpha,d) in peaks] - peaks=self._filterClose(peaks) - peaks.sort(key=lambda rc: rc[0]) - log.debug("detected peaks: %s",peaks) + img=self._createImg() + (ab,cd)=self._detectLines() + for (score,alpha,beta) in (ab,cd): + log.debug("score: %s",score) + log.debug("alpha, beta: %s, %s",alpha,beta) + cv.line(img,(0,alpha),(self._diagLen-1,beta),(0,255,255)) - 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])) - - for (i,line) in enumerate(lines): - self.drawLine(img,line,i) self.show(img) def update(self,img,weight=1): @@ -192,6 +110,28 @@ class HoughTransform: log.debug("dominant angles: %s, %s",alpha,beta) return (alpha[0],beta[0]) + def _detectLines(self): + bag=LineBag() + for alpha in range(0,180,2): + for beta in range(alpha-45,alpha+45,2): + accLine=self._readLine(alpha,beta) + (peaks,props)=scipy.signal.find_peaks(accLine,prominence=0) + prominences=sorted(props["prominences"],reverse=True)[:19] + bag.put(sum(prominences),alpha,beta) + return bag.pull(2) + + def _readLine(self,alpha,beta): + n=self._diagLen-1 + res=[] + for i in range(n+1): + k=round((alpha*(n-i)+beta*i)/n) + if k<0 or k>=180: + if k<-180 or k>360: print(alpha,beta,i,k) + k=k%180 + i=n+1-i + res.append(self._acc[k][i]) + return res + def _computeGridParams(self,lines): log.debug("computing grid parameters for: %s",lines) angles=[alpha for (alpha,d) in lines]