PictureEdit/transform_utils.py

111 lines
3.3 KiB
Python
Raw Normal View History

2025-09-02 16:49:39 +08:00
import math
import cv2
import numpy as np
class ResizeHandle:
"""图片缩放控制点"""
def __init__(self, position):
# 位置: 'tl', 'tr', 'bl', 'br' 分别代表四个顶点
self.position = position
self.active = False
self.x = 0
self.y = 0
self.radius = 5 # 控制点半径从8减小到5
def hit_test(self, x, y):
"""检查点是否在控制范围内"""
dx = x - self.x
dy = y - self.y
return math.sqrt(dx * dx + dy * dy) <= self.radius
class TransformHelper:
"""图片变形辅助工具"""
@staticmethod
def calculate_handles(fg, canvas):
"""计算四个顶点的控制点位置"""
handles = {
'tl': ResizeHandle('tl'),
'tr': ResizeHandle('tr'),
'bl': ResizeHandle('bl'),
'br': ResizeHandle('br')
}
# 转换顶点坐标到画布坐标
for i, pos in enumerate(['tl', 'tr', 'bl', 'br']):
x, y = fg.vertices[i]
canvas_x, canvas_y = canvas.template_to_canvas(x, y)
handles[pos].x = canvas_x
handles[pos].y = canvas_y
return handles
@staticmethod
def update_vertices(fg, handle_pos, dx, dy, canvas):
"""根据拖动的控制点更新顶点坐标和图片尺寸"""
t_dx, t_dy = canvas.canvas_to_template(dx, dy)
idx_map = {'tl': 0, 'tr': 1, 'bl': 2, 'br': 3}
idx = idx_map[handle_pos]
# 更新对应顶点
x, y = fg.vertices[idx]
new_x, new_y = x + t_dx, y + t_dy
# 获取其他顶点
tl = fg.vertices[0] if idx != 0 else (new_x, new_y)
tr = fg.vertices[1] if idx != 1 else (new_x, new_y)
bl = fg.vertices[2] if idx != 2 else (new_x, new_y)
br = fg.vertices[3] if idx != 3 else (new_x, new_y)
# 计算宽度和高度
width = max(abs(tr[0] - tl[0]), abs(br[0] - bl[0]))
height = max(abs(bl[1] - tl[1]), abs(br[1] - tr[1]))
# 确保最小尺寸
if width < 10 or height < 10:
# 如果太小,则不更新
return fg
fg.vertices[idx] = (new_x, new_y)
return fg
@staticmethod
def get_perspective_matrix(src_vertices, dst_size):
"""计算透视变换矩阵"""
# 源坐标:图片四个角
src = np.float32([
[0, 0],
[dst_size[0], 0],
[0, dst_size[1]],
[dst_size[0], dst_size[1]]
])
# 目标坐标:变形后的四个角
dst = np.float32(src_vertices)
# 计算透视变换矩阵
return cv2.getPerspectiveTransform(src, dst)
@staticmethod
def update_image_size(fg, handle_pos, dx, dy, canvas):
"""根据拖动的控制点更新图片尺寸"""
t_dx, t_dy = canvas.canvas_to_template(dx, dy)
if handle_pos in ['nw', 'sw', 'w']:
fg.x += t_dx
fg.w -= t_dx
if handle_pos in ['ne', 'se', 'e']:
fg.w += t_dx
if handle_pos in ['nw', 'ne', 'n']:
fg.y += t_dy
fg.h -= t_dy
if handle_pos in ['sw', 'se', 's']:
fg.h += t_dy
# 确保尺寸为正值
fg.w = max(10, fg.w)
fg.h = max(10, fg.h)
return fg