目录
快速求N个点到一条直线的距离
Python, OpenCV, Numpy
def get_distance_line_and_p_batch(line, p1_list):
"""
求点到直线距离
:param line: array([[vx], [vy], [x0], [y0]], dtype=float32)
:param p1_list: 线外点
:return:
每个线外点到直线距离(垂直)
每个线外点在直线垂线交点的X
每个线外点在直线垂线交点的Y
"""
vx, vy, x0, y0 = line
vx = vx[0]
vy = vy[0]
x0 = x0[0]
y0 = y0[0]
B0 = [vy * x0 - vx * y0] * len(p1_list)
B1 = [vx * p1[0] + vy * p1[1] for p1 in p1_list]
A = np.array([[vy, -vx], [vx, vy]], dtype=np.float32)
B = np.array([B0, B1], dtype=np.float32)
ret, X = cv2.solve(A, B, flags=cv2.DECOMP_LU)
x2 = X[0, :]
y2 = X[1, :]
x1 = np.array([p1[0] for p1 in p1_list], dtype=np.float32)
y1 = np.array([p1[1] for p1 in p1_list], dtype=np.float32)
return ((x2 - x1) ** 2 + (y2 - y1) ** 2) ** 0.5, x2, y2
cv2.minAreaRect函数及旋转框后处理方法
求旋转框的长边、短边、旋转角度、关键点坐标
def find_rotRect_long_side(rotRect):
(cx, cy), (cw, ch), angle = rotRect
cw2d, ch2d, theta = cw / 2, ch / 2, np.pi * angle / 180 # cw/2, ch/2, 旋转角(弧度制)
cA = (cx - cw2d * np.cos(theta), cy - cw2d * np.sin(theta))
cB = (cx + cw2d * np.cos(theta), cy + cw2d * np.sin(theta))
cC = (cx - ch2d * np.sin(theta), cy + ch2d * np.cos(theta))
cD = (cx + ch2d * np.sin(theta), cy - ch2d * np.cos(theta))
# cAB = np.sqrt((cA[0] - cB[0]) ** 2 + (cA[1] - cB[1]) ** 2) # ==cw
# cCD = np.sqrt((cC[0] - cD[0]) ** 2 + (cC[1] - cD[1]) ** 2) # ==ch
point_AD = (cA[0] + ch2d * np.sin(theta), cA[1] - ch2d * np.cos(theta)) # A和D的所在直线的交点,旋转框的一个角点坐标
point_AC = (cA[0] - ch2d * np.sin(theta), cA[1] + ch2d * np.cos(theta))
point_BD = (cB[0] + ch2d * np.sin(theta), cB[1] - ch2d * np.cos(theta))
point_BC = (cB[0] - ch2d * np.sin(theta), cB[1] + ch2d * np.cos(theta))
if cw > ch: # cAB > cCD, cw > ch
point_start = cA
point_end = cB
long_length = float(cw)
long_angle = angle
point_upper_long_line = cD
point_lower_long_line = cC # cC在cD左下方
short_length = float(ch)
else:
point_start = cC
point_end = cD
long_length = float(ch)
long_angle = angle - 90 # 逆时针
point_upper_long_line = cA
point_lower_long_line = cB
short_length = float(cw)
# point_start/point_end: 长边方向的起始点和结束点,起始点总是在结束点的左侧
# point_upper_long_line/point_lower_long_line: 短边方向的两个点,被长边直线隔开
return dict(point_start=point_start, point_end=point_end, point_center=(cx, cy),
long_length=long_length, long_angle=long_angle, short_length=short_length,
point_upper_long_line=point_upper_long_line, point_lower_long_line=point_lower_long_line,
point_AD=point_AD, point_AC=point_AC, point_BD=point_BD, point_BC=point_BC)
裁剪旋转框(最小旋正角度抠图)
def crop_minAreaRect_with_min_angle(rot3: Tuple, img: np.ndarray):
(cx, cy), (cw, ch), angle = rot3 # angle属于(0,90],为x轴(矩形最低点) 顺时针 旋转的角度
height, width = img.shape[:2]
# min_angle = angle if angle < 45 else angle - 90 # min_angle为正,则逆时针旋转图像
if angle < 45:
w, h = cw, ch # 逆时针旋转跟x轴重合
rotate_matrix = cv2.getRotationMatrix2D(center=(cx, cy), angle=angle, scale=1)
else:
w, h = ch, cw # 顺时针旋转跟x轴重合
rotate_matrix = cv2.getRotationMatrix2D(center=(cx, cy), angle=angle - 90, scale=1)
rotated_image = cv2.warpAffine(src=img, M=rotate_matrix, dsize=(width, height))
x0, y0 = max(0, int(cx - w/2 + 0.5)), max(0, int(cy - h/2 + 0.5)) # 左上角xy,注意左上角可能越界
x1, y1 = min(width, int(cx + w/2 + 0.5)), min(height, int(cy + h/2 + 0.5)) # 右下角xy
return rotated_image[y0:y1, x0:x1]
cv2.fitLine函数及后处理方法
穿过图像画直线
def draw_line_crossImg(img: np.ndarray, line, scale_xy=1.0, color=(0,0,0)):
"""
穿过整张图,画一条直线
:param line: fitLine的结果,array([[vx], [vy], [x0], [y0]], dtype=float32)
:param scale_xy: x0, y0的缩放比例
"""
h, w = img.shape[:2]
vx, vy, x0, y0 = line
vx = vx[0]
vy = vy[0]
x0 = x0[0] * scale_xy
y0 = y0[0] * scale_xy
pts = []
dxy = vx / (vy + 1e-7)
for y1 in [0, h-1]:
x1 = x0 + (y1 - y0) * dxy
if 0 <= x1 < w: pts.append((x1, y1))
if len(pts) < 2:
dyx = vy / (vx + 1e-7)
for x1 in [0, w-1]:
y1 = y0 + (x1 - x0) * dyx
if 0 <= y1 < h: pts.append((x1, y1))
cv2.line(img, (round(pts[0][0]), round(pts[0][1])), (round(pts[1][0]), round(pts[1][1])), color)
return pts
计算两条直线交点
def calc_2lines_intersection(line1_params, line2_params):
"""
计算两直线交点,传入cv2.fitLine返回值格式的两条直线
Args:
line1_params:
line2_params:
# # 使用cv2.fitLine函数计算两条直线的参数
# line1_params = cv2.fitLine(np.array([line1_start, line1_end]), cv2.DIST_L2, 0, 0.01, 0.01)
# line2_params = cv2.fitLine(np.array([line2_start, line2_end]), cv2.DIST_L2, 0, 0.01, 0.01)
Returns:
"""
vx, vy, x0, y0 = line1_params
a1, b1, c1 = vy[0], -vx[0], vx[0] * y0[0] - vy[0] * x0[0]
vx, vy, x0, y0 = line2_params
a2, b2, c2 = vy[0], -vx[0], vx[0] * y0[0] - vy[0] * x0[0]
# 处理平行和垂直的特殊情况
delta = a2 * b1 - a1 * b2
if abs(delta) < 1e-10:
# 平行情况,返回None
return None
# 计算交点的x和y坐标
x_intersect = (b2 * c1 - b1 * c2) / delta
y_intersect = (a1 * c2 - a2 * c1) / delta
return x_intersect, y_intersect
提取图像内部的轮廓点
def extract_inside_pts_of_contour(contour, w, h, ):
"""
contour是在wxh大小的图中的一个轮廓,目的是提取内部轮廓点(剔除图边界上的轮廓点)
"""
cond_w = (2 < contour[:, 0, 0]) & (contour[:, 0, 0] < w - 2)
cond_h = (2 < contour[:, 0, 1]) & (contour[:, 0, 1] < h - 2)
# print(cond_w.shape, cond_h.shape, ) # (163,) (163,)
return contour[cond_w & cond_h] # 剔除边界上的轮廓点, (159, 1, 2)