做图像定位+关键点任务时,需要数据增样,遂写了这么一篇
当时只有三个关键点,就写的比较面向过程,想优化/加点的同学就交给你们了!
任务就三步:
①imutils.rotate_bound无损旋转图片
②重新计算坐标
③裁剪无用区域
裁剪空白区域的方法来自这位同学的crop_img_empty,在其之上做了一些修改
https://blog.csdn.net/soderayer/article/details/123467979
import math
import cv2
import imutils
from crop_empty_zone import crop_img_empty
# 旋转图片后(顺时针),重新计算图片宽高+关键点位置
# 输入:CV图片,旋转角度,三个关键点坐标(已归一化)
# 输出:旋转完成图片,关键点新坐标
def rotate_img_location(img, angle, point_box):
# 第一步:计算新宽高
h, w = img.shape[0], img.shape[1] # 注意:CV是高宽不是宽高
radian = math.radians(angle) # 角度转弧度
new_w = abs(w * math.cos(radian)) + abs(h * math.sin(radian)) # 要加绝对值!!!!
new_h = abs(h * math.cos(radian)) + abs(w * math.sin(radian)) # 要加绝对值!!!!!!!!
# 第二步:计算新关键点位置
x1, y1, x2, y2, x3, y3 = point_box
x1, y1, x2, y2, x3, y3 = x1 * w, y1 * h, x2 * w, y2 * h, x3 * w, y3 * h # 对归一化的数据进行还原
old_center = w / 2, h / 2
x1, y1 = rotate_point_on_point((x1, y1), old_center, angle) # 计算旋转后关键点坐标
x2, y2 = rotate_point_on_point((x2, y2), old_center, angle)
x3, y3 = rotate_point_on_point((x3, y3), old_center, angle)
new_o_x, new_o_y = (new_w - w) / 2, (new_h - h) / 2 # 新坐标系与原坐标系的差值
x1, y1, x2, y2, x3, y3 = x1 + new_o_x, y1 + new_o_y, x2 + new_o_x, y2 + new_o_y, x3 + new_o_x, y3 + new_o_y
# 第三步:顺时针旋转,rotate_bound保证图像旋转后完整,确保整个图都在视野范围
rotated = imutils.rotate_bound(img, angle)
# 第四步:裁掉空像素,并重新定位原点位置
rotated, (o_x, o_y) = crop_img_empty(rotated)
x1, y1, x2, y2, x3, y3 = int(x1 - o_x), int(y1 - o_y), int(x2 - o_x), int(y2 - o_y), int(x3 - o_x), int(y3 - o_y)
# 预览结果
cv2.circle(rotated, (x1, y1), 1, (0, 0, 255), 2)
cv2.circle(rotated, (x2, y2), 1, (0, 255, 0), 2)
cv2.circle(rotated, (x3, y3), 1, (255, 0, 0), 2)
cv2.imshow("Rotated Without Cropping", rotated)
cv2.waitKey(10)
return rotated, (x1, y1, x2, y2, x3, y3)
# 计算某点绕另一点顺时针旋转后位置
# 输入:动点、中心点、角度
# 输出:动点新坐标
def rotate_point_on_point(point, center, angle):
src_x, src_y = point
center_x, center_y = center
radian = math.radians(angle)
# 顺时针
dest_x = (src_x - center_x) * math.cos(radian) - (src_y - center_y) * math.sin(radian) + center_x
dest_y = (src_x - center_x) * math.sin(radian) + (src_y - center_y) * math.cos(radian) + center_y
# 逆时针
# dest_x = (src_x - center_x) * math.cos(radian) + (src_y - center_y) * math.sin(radian) + center_x
# dest_y = (src_y - center_y) * math.cos(radian) - (src_x - center_x) * math.sin(radian) + center_y
return int(dest_x), int(dest_y)
# 裁剪图像空白区域
# 输入:cv图片对象
# 输出:裁剪后图片(cv),原点坐标偏移量(x, y)
def crop_img_empty(ImageArray):
# image = Image.fromarray(cv2.cvtColor(ImageArray,cv2.COLOR_BGR2RGB)) # 打开tiff图像
row = ImageArray.shape[0]
col = ImageArray.shape[1]
# print(row, col)
# 先计算所有图片的裁剪范围,然后再统一裁剪并输出图片
x_left = row
x_top = col
x_right = 0
x_bottom = 0
# 上下左右范围
"""
Image.crop(left, up, right, below)
left:与左边界的距离
up:与上边界的距离
right:还是与左边界的距离
below:还是与上边界的距离
简而言之就是,左上右下。
"""
for r in range(row):
for c in range(col):
# if ImageArray[row][col][0] < 255 or ImageArray[row][col][0] ==0:
if ImageArray[r][c][0] < 255 and ImageArray[r][c][0] != 0: # 外框有个黑色边框,增加条件判断
if x_top > r:
x_top = r # 获取最小x_top
if x_bottom < r:
x_bottom = r # 获取最大x_bottom
if x_left > c:
x_left = c # 获取最小x_left
if x_right < c:
x_right = c # 获取最大x_right
print(x_left, x_top, x_right, x_bottom)
# 原为保留-5的裕量(如x_left - 5)。此处为了保证精度而去掉
# cropped = image.crop((x_left, x_top, x_right, x_bottom)) # (left, upper, right, lower) PIL裁剪
cropped = ImageArray[x_top:x_bottom, x_left:x_right] # CV裁剪
# cropped.save(r"C:\Users\Administrator\Desktop\out_cut_bg\{}.png".format(imageName[:-4], i))
print("completed!")
return cropped, (x_left, x_top)
if __name__ == '__main__':
image = cv2.imread("0.png")
point_location = (0.2, 0.38, 0.25, 0.45, 0.35, 0.61)
rotate_angle = 90
# rotate_img_location(image, rotate_angle, point_location)
for i in range(360):
rotate_img_location(image, i, point_location)