python opencv 直线检测
python opencv直线检测与拟合
·
先创建一个“点”类和一个“直线”类
class Endpoint:
def __init__(self, x=0.0, y=0.0):
self.x = x
self.y = y
from endpoint import Endpoint
class Line:
def __init__(self, p1, p2):
self.p1 = p1
self.p2 = p2
self.k = (p1.y - p2.y) / (p1.x - p2.x)
self.b = p1.y - self.k * p1.x
self.catagory = 0
def set_line_segment_end(self, p1, p2):
self.segment_p1 = p1
self.segment_p2 = p2
对图像依次进行:转灰度图、二值化、锐化、霍夫变换、直线拟合 几项操作。
转灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
采用大津自适应二值化算法
retval, dst = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
锐化,为了让直线边界更清楚
# 锐化
kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]], np.float32) # 定义一个核
dst2 = cv2.filter2D(dst, -1, kernel=kernel)
创建一个三通道的黑白图,方便把拟合的直线画在上面,查看结果用,二值化的图为单通道上面不能画彩色的线
dst3 = cv2.cvtColor(dst2, cv2.COLOR_GRAY2BGR)
做霍夫变换
lines = cv2.HoughLinesP(dst2, 1, np.pi / 180, 100, 100, 10)
对霍夫变换检测出来的线进行处理,创建成我们自己创建的“直线”类。
for i in range(lines.shape[0]):
for x1, y1, x2, y2 in lines[i]:
p1 = Endpoint(x1, y1)
p2 = Endpoint(x2, y2)
line = Line(p1, p2)
if line.k > 0.1:
line.catagory = 1
lineList_1.append(line)
elif line.k < -0.1:
line.catagory = 3
lineList_3.append(line)
else:
if line.b < img.shape[0] / 2:
line.catagory = 2
lineList_2.append(line)
elif line.b > img.shape[0] / 2:
line.catagory = 4
lineList_4.append(line)
kList.append(line.k)
bList.append(line.b)
霍夫变换检测出来是一堆小短线,我们需要的结果是一根完整的长线,对这些短线进行拟合,拟合的方法是,取所有线段的端点,对端点进行加权最小二乘拟合,得到我们想要的线。
加权最小二乘拟合公式如下

import numpy as np
def weighted_least_square_method(X, Y, W):
Y = Y[:, np.newaxis]
ones = np.ones((len(X), 1))
X = np.hstack((X[:, np.newaxis], ones))
temp = np.matmul(W, X)
temp2 = np.linalg.inv(np.matmul(X.T, temp))
temp3 = np.matmul(W, Y)
temp4 = np.matmul(X.T, temp3)
theta = np.matmul(temp2, temp4)
return theta
拟合之前还有一步剔除离群点
import numpy as np
def detect_outliers(data, threshold=2):
"""离群点检测"""
mean_d = np.mean(data)
std_d = np.std(data)
outliers = []
for y in data:
z_score = (y - mean_d) / std_d
if np.abs(z_score) > threshold:
outliers.append(y)
idx = []
for i in range(len(data)):
for j in range(len(outliers)):
if data[i] == outliers[j]:
idx.append(i)
return outliers, idx
超过标准差一定倍数(threshold)的点,认为是离群点剔除。
完整代码:
import cv2
import numpy as np
import matplotlib.pyplot as plt
from endpoint import Endpoint
from line import Line
from find_isolation import detect_outliers
from weighted_lsm import weighted_least_square_method
global img
def mouse(event, x, y, flags, param):
global img
if event == cv2.EVENT_LBUTTONDOWN:
xy = "%d,%d" % (x, y)
cv2.circle(img, (x, y), 1, (255, 255, 255), thickness = -1)
cv2.putText(img, xy, (x, y), cv2.FONT_HERSHEY_PLAIN,
1.0, (255, 255, 255), thickness = 1)
cv2.imshow("image", img)
def line_fit(lineList, img, type):
X_ = []
Y_ = []
for line in lineList:
X_.append(line.p1.x)
X_.append(line.p2.x)
Y_.append(line.p1.y)
Y_.append(line.p2.y)
# cv2.line(img, (int(line.p1.x), int(line.p1.y)), (int(line.p2.x), int(line.p2.y)), (255, 0, 0), 1)
cv2.circle(img, (line.p1.x, line.p1.y), 1, (255, 0, 0), 2)
cv2.circle(img, (line.p2.x, line.p2.y), 1, (255, 0, 0), 2)
XX = np.array(X_)
YY = np.array(Y_)
outliers_X, outliers_X_indx = detect_outliers(XX)
X = np.delete(XX, outliers_X_indx)
Y = np.delete(YY, outliers_X_indx)
W = np.eye(len(X))
for i in range(len(X)):
W[i, i] = X[i]
if type == 2 or type == 4:
[k, b] = np.polyfit(X, Y, deg=1)
else:
[k, b] = weighted_least_square_method(X, Y, W)
p1 = Endpoint(0, b)
p2 = Endpoint(3000, k * 3000 + b)
line_fit = Line(p1, p2)
p1_segment = Endpoint(min(X), k * min(X) + b)
p2_segment = Endpoint(max(X), k * max(X) + b)
line_fit.set_line_segment_end(p1_segment, p2_segment)
cv2.line(img, (int(line_fit.p1.x), int(line_fit.p1.y)), (int(line_fit.p2.x), int(line_fit.p2.y)), (255, 255, 0), 1)
return line_fit
def find_crosspoint(addr):
print(addr)
global img
img = cv2.imread(addr)
# 灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# canny = cv2.Canny(gray, 100, 200)
# 二值化
# dst = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 25, 5)
retval, dst = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
# 腐蚀
# kernel = np.ones((3, 3), np.uint8)
# erosion = cv2.erode(dst, kernel, iterations=1)
# erosion2 = cv2.cvtColor(erosion, cv2.COLOR_GRAY2BGR)
# ##膨胀
# kernel_d = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# dilated = cv2.dilate(dst, kernel_d)
# 锐化
kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]], np.float32) # 定义一个核
dst2 = cv2.filter2D(dst, -1, kernel=kernel)
dst3 = cv2.cvtColor(dst2, cv2.COLOR_GRAY2BGR)
# 霍夫线变换
lines = cv2.HoughLinesP(dst2, 1, np.pi / 180, 100, 100, 10)
lineList_1 = []
lineList_2 = []
lineList_3 = []
lineList_4 = []
kList = []
bList = []
# 将检测的线绘制
for i in range(lines.shape[0]):
for x1, y1, x2, y2 in lines[i]:
p1 = Endpoint(x1, y1)
p2 = Endpoint(x2, y2)
line = Line(p1, p2)
if line.k > 0.1:
line.catagory = 1
lineList_1.append(line)
elif line.k < -0.1:
line.catagory = 3
lineList_3.append(line)
else:
if line.b < img.shape[0] / 2:
line.catagory = 2
lineList_2.append(line)
elif line.b > img.shape[0] / 2:
line.catagory = 4
lineList_4.append(line)
kList.append(line.k)
bList.append(line.b)
l1 = line_fit(lineList_1, img, 1)
l2 = line_fit(lineList_2, img, 2)
l3 = line_fit(lineList_3, img, 3)
l4 = line_fit(lineList_4, img, 4)
# find corss point
x_up = (l2.b - l1.b) / (l1.k - l2.k)
y_up = l1.k * x_up + l1.b
p_up = Endpoint(x_up, y_up)
x_down = (l4.b - l3.b) / (l3.k - l4.k)
y_down = l3.k * x_down + l3.b
p_down = Endpoint(x_down, y_down)
# find end point
p_rightUP = l2.segment_p1
p_rightDown = l4.segment_p1
dis_parallel_real = 63
base_dis_img = l4.b - l2.b
dis_img_up = l2.segment_p1.x - p_up.x
dis_real_up = (dis_img_up / base_dis_img) * dis_parallel_real
dis_real_down = l4.segment_p1.x - p_down.x
dis_real_down = (dis_real_down / base_dis_img) * dis_parallel_real
dis_real = (dis_real_up + dis_real_down) / 2
cv2.circle(img, (int(p_up.x), int(p_up.y)), 1, (0, 255, 0), 2)
cv2.circle(img, (int(p_down.x), int(p_down.y)), 1, (0, 255, 0), 2)
cv2.circle(img, (int(p_rightUP.x), int(p_rightUP.y)), 1, (0, 255, 0), 2)
cv2.circle(img, (int(p_rightDown.x), int(p_rightDown.y)), 1, (0, 255, 0), 2)
print(dis_real, dis_real_up, dis_real_down)
cv2.imshow("img", img)
cv2.setMouseCallback("img", mouse)
cv2.waitKey(0)
return p_up, p_down
name_list = [""]
for name_i in name_list:
# addr = 注意这里路径不能有中文
p_up, p_down = find_crosspoint(addr)
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐



所有评论(0)