Kindai-OCR/coordinates.py

138 lines
4.9 KiB
Python

import numpy as np
def fit_line(p1, p2):
# fit a line ax+by+c = 0
if p1[0] == p1[1]:
return [1., 0., -p1[0]]
else:
[k, b] = np.polyfit(p1, p2, deg=1)
return [k, -1., b]
def line_cross_point(line1, line2):
# line1 0= ax+by+c, compute the cross point of line1 and line2
if line1[0] != 0 and line1[0] == line2[0]:
print('Cross point does not exist')
return None
if line1[0] == 0 and line2[0] == 0:
print('Cross point does not exist')
return None
if line1[1] == 0:
x = -line1[2]
y = line2[0] * x + line2[2]
elif line2[1] == 0:
x = -line2[2]
y = line1[0] * x + line1[2]
else:
k1, _, b1 = line1
k2, _, b2 = line2
x = -(b1-b2)/(k1-k2)
y = k1*x + b1
return np.array([x, y], dtype=np.float32)
def line_verticle(line, point):
# get the verticle line from line across point
if line[1] == 0:
verticle = [0, -1, point[1]]
else:
if line[0] == 0:
verticle = [1, 0, -point[0]]
else:
verticle = [-1./line[0], -1, point[1] - (-1/line[0] * point[0])]
return verticle
def rectangle_from_parallelogram(poly):
'''
fit a rectangle from a parallelogram
:param poly:
:return:
'''
p0, p1, p2, p3 = poly
angle_p0 = np.arccos(np.dot(p1-p0, p3-p0)/(np.linalg.norm(p0-p1) * np.linalg.norm(p3-p0)))
if angle_p0 < 0.5 * np.pi:
if np.linalg.norm(p0 - p1) > np.linalg.norm(p0-p3):
# p0 and p2
## p0
p2p3 = fit_line([p2[0], p3[0]], [p2[1], p3[1]])
p2p3_verticle = line_verticle(p2p3, p0)
new_p3 = line_cross_point(p2p3, p2p3_verticle)
## p2
p0p1 = fit_line([p0[0], p1[0]], [p0[1], p1[1]])
p0p1_verticle = line_verticle(p0p1, p2)
new_p1 = line_cross_point(p0p1, p0p1_verticle)
return np.array([p0, new_p1, p2, new_p3], dtype=np.float32)
else:
p1p2 = fit_line([p1[0], p2[0]], [p1[1], p2[1]])
p1p2_verticle = line_verticle(p1p2, p0)
new_p1 = line_cross_point(p1p2, p1p2_verticle)
p0p3 = fit_line([p0[0], p3[0]], [p0[1], p3[1]])
p0p3_verticle = line_verticle(p0p3, p2)
new_p3 = line_cross_point(p0p3, p0p3_verticle)
return np.array([p0, new_p1, p2, new_p3], dtype=np.float32)
else:
if np.linalg.norm(p0-p1) > np.linalg.norm(p0-p3):
# p1 and p3
## p1
p2p3 = fit_line([p2[0], p3[0]], [p2[1], p3[1]])
p2p3_verticle = line_verticle(p2p3, p1)
new_p2 = line_cross_point(p2p3, p2p3_verticle)
## p3
p0p1 = fit_line([p0[0], p1[0]], [p0[1], p1[1]])
p0p1_verticle = line_verticle(p0p1, p3)
new_p0 = line_cross_point(p0p1, p0p1_verticle)
return np.array([new_p0, p1, new_p2, p3], dtype=np.float32)
else:
p0p3 = fit_line([p0[0], p3[0]], [p0[1], p3[1]])
p0p3_verticle = line_verticle(p0p3, p1)
new_p0 = line_cross_point(p0p3, p0p3_verticle)
p1p2 = fit_line([p1[0], p2[0]], [p1[1], p2[1]])
p1p2_verticle = line_verticle(p1p2, p3)
new_p2 = line_cross_point(p1p2, p1p2_verticle)
return np.array([new_p0, p1, new_p2, p3], dtype=np.float32)
def sort_rectangle(poly):
# sort the four coordinates of the polygon, points in poly should be sorted clockwise
# First find the lowest point
p_lowest = np.argmax(poly[:, 1])
if np.count_nonzero(poly[:, 1] == poly[p_lowest, 1]) == 2:
# 底边平行于X轴, 那么p0为左上角 - if the bottom line is parallel to x-axis, then p0 must be the upper-left corner
p0_index = np.argmin(np.sum(poly, axis=1))
p1_index = (p0_index + 1) % 4
p2_index = (p0_index + 2) % 4
p3_index = (p0_index + 3) % 4
return poly[[p0_index, p1_index, p2_index, p3_index]], 0.
else:
# 找到最低点右边的点 - find the point that sits right to the lowest point
p_lowest_right = (p_lowest - 1) % 4
p_lowest_left = (p_lowest + 1) % 4
angle = np.arctan(-(poly[p_lowest][1] - poly[p_lowest_right][1])/(poly[p_lowest][0] - poly[p_lowest_right][0]))
# assert angle > 0
# if angle <= 0:
# print(angle, poly[p_lowest], poly[p_lowest_right])
if angle/np.pi * 180 > 45:
# 这个点为p2 - this point is p2
p2_index = p_lowest
p1_index = (p2_index - 1) % 4
p0_index = (p2_index - 2) % 4
p3_index = (p2_index + 1) % 4
return poly[[p0_index, p1_index, p2_index, p3_index]], -(np.pi/2 - angle)
else:
# 这个点为p3 - this point is p3
p3_index = p_lowest
p0_index = (p3_index + 1) % 4
p1_index = (p3_index + 2) % 4
p2_index = (p3_index + 3) % 4
return poly[[p0_index, p1_index, p2_index, p3_index]], angle