参考:https://mp.weixin.qq.com/s/eOLTnrIPaMoiRneN_CbaIA
找到图像的横线开始和结束坐标,并计算角度
图像预处理
import cv2
img = cv2.imread('***.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
binary = cv2.adaptiveThreshold(~gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 15, -5)
# 找到横线
x_scale = 20 #设置参数
rows_z, cols_z = binary.shape
size = (cols_z // x_scale, 1)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, size)
eroded = cv2.erode(binary, kernel, iterations=1) # 腐蚀
dilated_col_z = cv2.dilate(eroded, kernel, iterations=1) # 膨胀
计算角度
def cal_angle(x1,y1,x2,y2):
if x2 - x1 == 0:
self.logger.info("直线是竖直的")
result = 90
elif y2 - y1 == 0:
self.logger.info("直线是水平的")
result = 0
else:
# 计算斜率
k = -(y2 - y1) / (x2 - x1)
# 求反正切,再将得到的弧度转换为度
result = np.arctan(k) * 57.29577 # 逆时针
result = round(result,3)#保留三位数
return result
获取去重后的坐标点
def drop_duplicated_row_points(pos, max_span):
sort_point = np.sort(list(set(pos)))
point_arr = [sort_point[0]] # 每种类型数据max_span行都不一样
for i in range(1, len(sort_point) - 1):
if sort_point[i] - point_arr[-1] > max_span:
point_arr.append(sort_point[i])
return point_arr
Hough,HoughP(效果不太好)
HoughLines
lines = cv2.HoughLines(dilated_col_z, 1, np.pi / 180, 100) # θ的精度np.pi / 180
# 绘制直线
test_img = img.copy()
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))
cv2.line(test_img, (x1, y1), (x2, y2), (0, 0, 255), 1)
HoughLinesP, HoughP_line是Hough_line算法的改进版
lines = cv2.HoughLinesP(image=dilated_col_z,
rho=1,
theta=np.pi / 180,
threshold=100,
minLineLength=5, # 能组成一条直线的最少点的数量,点数量不足的直线将被抛弃。
maxLineGap=1 # 被认为在一条直线上的点的最大距离
)
for line in lines:
x1, y1, x2, y2 = line[0]
test_img = img.copy()
cv2.line(test_img, (x1, y1), (x2, y2), (0, 255, 0), 2)
lsd算法(推荐,效果不错)
注意:opencv版本退到3.4
:pip install “opencv-python-headless<4.3”
LSD算法通过对图像局部分析,得出直线的像素点集,再通过假设参数进行验证求解,将像素点集合与误差控制集合合并,进而自适应控制误检的数量。
lsd = cv2.createLineSegmentDetector(0,1)
# 通过横线计算角度
rows_z, cols_z = binary.shape
size = (cols_z // 20, 1)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, size)
eroded = cv2.erode(binary, kernel, iterations=1) # 腐蚀
ys, xs = np.where(eroded > 0)
rows_list = drop_duplicated_row_points(ys, max_span=50) # 记录横线坐标
# 第一条直线开始和结束纵坐标
sort_ys = sorted(list(set(ys)))
first_line_y_end = sort_ys[sort_ys.index(rows_list[1]) - 1]
first_line_y_start = rows_list[0]
dlines = lsd.detect(eroded[first_line_y_start:first_line_y_end, :])[0]
pos = [[x[0][0], x[0][1]] for x in dlines] + [[x[0][2], x[0][3]] for x in dlines]
top_pos_list = [x for x in pos if x[0] == min([x[0] for x in pos])] # 最小的x
top_pos = [x for x in top_pos_list if x[1] == min([x[1] for x in top_pos_list])][0] # 最小的y
bottom_pos_list = [x for x in pos if x[0] == max([x[0] for x in pos])] # 最大的x
bottom_pos = [x for x in bottom_pos_list if x[1] == max([x[1] for x in bottom_pos_list])][0] # 最小的y
x1, y1, x2, y2 = top_pos + bottom_pos
angle = cal_angle(x1, y1, x2, y2)