def cluster_by_rho(theta_groups): result_group = [] # 合并后的分组线 rho_differ = 7 for gp in theta_groups: # 按 rho 的大小排序的 line 列表 rho_lines = sorted([li for li in gp], key=lambda li: li.rho) rho_ls = [li.rho for li in rho_lines] rho_diff = np.diff(rho_ls) # calculate diff in theta_ls which have been sorted rho_diff_idx = np.where(rho_diff > rho_differ)[0] + 1 # slice by std value rho_diff_idx = np.append(rho_diff_idx, [len(rho_lines)]) # line_ls 长度作为最后一个 rho_groups = [] # 按 rho 分组 gp_rho_ls = [] for i, idx in enumerate(rho_diff_idx): # 邻近的diff theta 分组 if i == 0: rho_groups.append(rho_lines[0: idx]) gp_rho_ls.append(rho_ls[0: idx]) else: rho_groups.append(rho_lines[rho_diff_idx[i - 1]:idx]) gp_rho_ls.append(rho_ls[rho_diff_idx[i - 1]:idx]) # 在分组内(rho 和 theta 都接近的情况下) new_lines = [] for lis in rho_groups: if len(lis) > 1: # 合并在同一直线上的直线段 pts = [] for li in lis: pts.extend(li.getLinePts(includeEndPt=True)) new_li = GLine().fit(pts) calculate_theta_rho(new_li) new_lines.append(new_li) elif len(lis) == 1: new_lines.append(lis[0]) result_group.append(new_lines) return result_group
def lineDetect(img, mode=0, threshold=-1, minLineLength=0, maxLineGap=0): img = cv2.cvtColor(img, code=cv2.COLOR_BGR2GRAY) if img.ndim == 3 else img lines = [] if mode == 0: # 霍夫直线检测 # 参数 rho:累加器距离分辨率, theta:累加器角度分辨率 threshold:投票阈值, minLineLength:直线的最小长度 maxLineGap:直线合并最大断开距离 datas = cv2.HoughLinesP(img, rho=1, theta=np.pi / 360., threshold=int(threshold), minLineLength=minLineLength, maxLineGap=maxLineGap) if datas is None: return lines for d in datas.reshape(-1, 4): line = GLine().initXY(*d) if line.getLen() <= 0: continue lines.append(line) elif mode == 1: # 直线检测器检测 # 直线检测器检测 cv2.createLineSegmentDetector # 参数1 refine(LSD_REFINE_STD,): 调优策略, cv2.LSD_REFINE_NONE:无调优策略, cv2.LSD_REFINE_STD:标准调优策略,将弧分解成多个小线段, cv2.LSD_REFINE_ADV:使用NFA指标评价检测直线,调整参数近一步调优检测直线 # 参数2 scale(0.8): 缩放图像比例 参数3 sigma_scale(0.6): 高斯模糊核sigma=sigma_scale/scale 参数4 quant(2.0):梯度量化误差 # 参数5 ang_th(22.5):直线段角度容差 参数6 log_eps(0): NFA检测阈值 参数7 density_th(0.7):直线段密度阈值 参数8 n_bins(1024):梯度归一化数量 lsd = cv2.createLineSegmentDetector(cv2.LSD_REFINE_STD) datas, width, prec, nfa = lsd.detect(img) if datas is None and width is None and prec is None and nfa is None: print("检测失败,未能检测到直线");return lines datas = datas.reshape(-1, 4) width = width.reshape(-1).tolist() prec = prec.reshape(-1).tolist() for i in range(len(datas)): line = GLine().initXY(*datas[i]) if line.getLen() <= 0: continue line.width, line.prec = width[i], prec[i] lines.append(line) else: pass return lines
def fuse(baseline, line): tlen = baseline.getLen() + line.getLen() lineTh = max(tlen * maxRatio, minDis) # 直线间隔阈值 segTh = tlen * segRatio # 线段间隔阈值 pts = [line.pt1, line.pt2] lineDis = [baseline.distance(p, mode=0) for p in pts] # 直线距离取最大 segDis = [baseline.distance(p, mode=1) for p in pts] # 线段距离取最小 # 直线距离超过阈值, 或者断开距离超过阈值, 不能融合 if max(lineDis) > lineTh or min(segDis) > segTh: return False pt1, pt2, dis = getMaxLinesLen([baseline, line]) if abs(dis - baseline.getLen()) < 0.01: return True pts = baseline.getLinePts(True) pts.extend(line.getLinePts(True)) resline = GLine().fit(pts) pt1 = resline.getVertPt(pt1) pt2 = resline.getVertPt(pt2) baseline.initPts(pt1, pt2) return True
def isParallel(self, line, PDDR=0.05, overlapRatio=0.3, lineMindis=-1, lineMaxdis=-1): # 考虑因素,直线距离, 重合率, 线段长度差异, 平行距离差异 longLine, shortLine = (self, line) if self.getLen() > line.getLen() else (line, self) llen, slen = longLine.getLen(), shortLine.getLen() if float(slen) / llen < overlapRatio: return False pts = [shortLine.pt1, shortLine.pt2] lineDis = [longLine.distance(p, mode=0) for p in pts] # 直线距离 lineDif = lineDis[0] + lineDis[1] if longLine.isCross(shortLine) else abs(lineDis[0] - lineDis[1]) # 线是否平行 if PDDR < 1 and lineDif > slen * PDDR: return False if PDDR > 1 and lineDif > PDDR: return False if overlapRatio <= 0: return True # 平行线的距离大于两线距离, 为干扰线 if sum(lineDis) / 2 > slen + llen: return False if lineMindis > 0 and lineDis < lineMindis: return False if lineMaxdis > 0 and lineDis > lineMaxdis: return False # 计算重合率 pt1 = longLine.getVertPt(shortLine.pt1) pt2 = longLine.getVertPt(shortLine.pt2) pt1.flag = longLine.isPtAtLineSeg(pt1) pt2.flag = longLine.isPtAtLineSeg(pt2) if pt1.flag and pt2.flag: return True # 完全重合的情况 if not pt1.flag and not pt2.flag: return False # 完全不重合的请教 # 计算重合率 tmpLine = GLine(pt1, pt2) pt3 = longLine.pt1 if tmpLine.isPtAtLineSeg(longLine.pt1) else longLine.pt2 dis1 = pt3.distance(pt1) if pt1.flag else pt3.distance(pt2) dis2 = pt1.distance(pt2) return dis1 / dis2 > overlapRatio
def disLine0(a, b): if (a == b).all(): return 0 linea = GLine().initXY(a[0], a[1], a[2], a[3]) lineb = GLine().initXY(b[0], b[1], b[2], b[3]) longline, shortline = (linea, lineb) if linea.getLen() > lineb.getLen() else (lineb, linea) pts = [shortline.pt1, shortline.pt2] dis = min([longline.distance(pt, mode=1) for pt in pts]) angle = longline.angle(shortline) if angle > minAngle and dis < minDis: dis = minDis # 当超过角度, 距离至少位mindis(使其不符号条件) return dis * (math.sin(math.radians(angle)) + 1)
def getExtendLine(self, ratio=1.5): midPt = self.midPoint() x1, y1 = self.pt1.x + (self.pt1.x - midPt.x) * ratio, self.pt1.y + (self.pt1.y - midPt.y) * ratio x2, y2 = self.pt2.x + (self.pt2.x - midPt.x) * ratio, self.pt2.y + (self.pt2.y - midPt.y) * ratio return GLine().initXY(x1, y1, x2, y2)
def getParaLine(self, pt): line = GLine() line.a, line.b, line.c = self.a, self.b, -self.a * pt.fx - self.b * pt.fy line.d = math.sqrt(line.a ** 2 + line.b ** 2) return line
def getYAngle(self): line = GLine() line.a, line.b, line.c = 1, 0, 0 return self.angle(line)
def getXAngle(self): line = GLine() line.a, line.b, line.c = 0, 1, 0 return self.angle(line)
def midLine(self, line): if checkAnyNone(self.pt1, self.pt2, line.pt1, line.pt2): return None if self.pt1.distance(line.pt1) > self.pt1.distance(line.pt2): return GLine(self.pt1.midpoint(line.pt2), self.pt2.midpoint(line.pt1)) else: return GLine(self.pt1.midpoint(line.pt1), self.pt2.midpoint(line.pt2))