def sort_box(points): ###对输入的4个坐标排序【top-left, top-right, bottom-right, bottom-left】
points = np.array(points)
xSorted = points[np.argsort(points[:,0]),:]
# grab the left-most and right-most points from the sorted
# x-roodinate points
leftMost = xSorted[:2, :]
rightMost = xSorted[2:, :]
if leftMost[0,1]!=leftMost[1,1]:
leftMost=leftMost[np.argsort(leftMost[:,1]),:]
else:
leftMost=leftMost[np.argsort(leftMost[:,0])[::-1],:]
(tl, bl) = leftMost
if rightMost[0,1]!=rightMost[1,1]:
rightMost=rightMost[np.argsort(rightMost[:,1]),:]
else:
rightMost=rightMost[np.argsort(rightMost[:,0])[::-1],:]
(tr,br)=rightMost
# return the coordinates in top-left, top-right,
# bottom-right, and bottom-left order
return np.array([tl, tr, br, bl], dtype="float32")
对box坐标进行左上角开始的顺时针排序
###根据透视变换将旋转的文本框纠正水平放置
def Perspective_img(boxes,image):
image_h,image_w,_ = image.shape
new_boxes = []
for index,arbitrary_points in enumerate(boxes):
h = pointDistance(arbitrary_points[0],arbitrary_points[1])
w = pointDistance(arbitrary_points[3],arbitrary_points[0])
rectangle = np.float32([[0, h], [0, 0], [w, 0], [w, h]])
M = cv2.getPerspectiveTransform(arbitrary_points, rectangle)
doc_dst = cv2.warpPerspective(image, M, (w, h))
cv2.imwrite(f'./test_rotate/rotate_img_{index}.jpg',doc_dst)
new_boxes.append(doc_dst)
return new_boxes
根据透视变换将旋转的文本框纠正水平放置
###根据文本框的方向得到整张图像的方向
def get_angle(boxes):
all_angles = []
for index,box in enumerate(boxes):
box = sort_box(box)
mid2 = (box[0] + box[3]) / 2
mid1 = (box[1] + box[2]) / 2
x = math.atan2(mid1[1]-mid2[1],mid1[0]-mid2[0]) ###先传递y坐标,后x坐标
angle = x * 180 / math.pi ###范围[-180,180]
all_angles.append(angle)
min_angle = int(min(all_angles))
max_angle = int(max(all_angles))
###划分的区间
interal = [x for x in range(min_angle,max_angle,3)]
if len(interal) > 1:
s = pd.cut(all_angles,bins=interal)
final_angle = int(pd.value_counts(s).idxmax().mid)
else:
final_angle = int((min_angle + max_angle) / 2)
return final_angle
根据多个文本框的中心线统计整张图像的角度
# 根据旋转angle角度,旋转图像,缺失背景白色(255, 255, 255)填充
def rotate_bound_white_bg(image,boxes, angle):
# grab the dimensions of the image and then determine the
# center
(h, w) = image.shape[:2]
(cX, cY) = (w // 2, h // 2)
# grab the rotation matrix (applying the negative of the
# angle to rotate clockwise), then grab the sine and cosine
# (i.e., the rotation components of the matrix)
# -angle位置参数为角度参数负值表示顺时针旋转; 1.0位置参数scale是调整尺寸比例(图像缩放参数),建议0.75
M = cv2.getRotationMatrix2D((cX, cY), angle, 1.0)
cos = np.abs(M[0, 0])
sin = np.abs(M[0, 1])
# compute the new bounding dimensions of the image
nW = int((h * sin) + (w * cos))
nH = int((h * cos) + (w * sin))
# adjust the rotation matrix to take into account translation
M[0, 2] += (nW / 2) - cX
M[1, 2] += (nH / 2) - cY
# perform the actual rotation and return the image
# borderValue 缺失背景填充色彩,此处为白色,可自定义
rotated_image = cv2.warpAffine(image, M, (nW, nH),borderValue=(255,255,255))
###根据仿射变化矩阵求新的坐标
rotated_boxes = copy.deepcopy(boxes)
for index_box,box_points in enumerate(rotated_boxes):
for index_point, point in enumerate(box_points):
new_point = np.sum(point* M[:2,:2],axis=1) + M[:,2]
rotated_boxes[index_box,index_point,:] = new_point
return rotated_image,rotated_boxes
根据图片的角度旋转图像,并对box的坐标也做相对应的旋转
以下是对旋转图像公式的推导
坐标通过旋转矩阵得到新的坐标公式
根据上面的公式旋转矩阵M得到
new_point = np.sum(point* M[:2,:2],axis=1) + M[:,2]
X_new = X_old * cos(a) + Y_old * sin(a) + dx
Y_new = X_old * -sin(a) + Y_old * cos(a) + dy