""" 作者:太一 微信:taiyi1224 邮箱:shuobo1224@qq.com """ import cv2 import numpy as np from PIL import Image, ImageDraw, ImageOps class ImageManager: """图像管理工具类,处理图像加载、变换和保存""" @staticmethod def load_image(file_path): """加载图像并转换为RGBA格式""" try: return Image.open(file_path).convert("RGBA") except Exception as e: print(f"加载图像失败: {e}") return None @staticmethod def apply_mask(image, shape): """应用遮罩到图像""" if shape == "rect": return image # 矩形不需要遮罩 # 创建与图像相同大小的透明遮罩 mask = Image.new('L', image.size, 0) draw = ImageDraw.Draw(mask) # 根据形状绘制遮罩 if shape == "round": # 圆形遮罩 draw.ellipse((0, 0, image.size[0], image.size[1]), fill=255) elif shape == "heart": # 心形遮罩(简化版) x, y = image.size points = [ (x // 2, y // 5), (x // 5, y // 2), (x // 5, y * 3 // 4), (x // 2, y * 4 // 5), (x * 4 // 5, y * 3 // 4), (x * 4 // 5, y // 2), (x // 2, y // 5) ] draw.polygon(points, fill=255) elif shape == "star": # 星形遮罩(简化版) x, y = image.size center_x, center_y = x // 2, y // 2 radius = min(center_x, center_y) points = [] for i in range(10): angle = 0.1 * i * 3.14159 r = radius if i % 2 == 0 else radius * 0.4 points.append(( center_x + r * (1 if i % 4 < 2 else -1) * abs(angle % (2 * 3.14159) - 3.14159 / 2) ** 0.5, center_y + r * (1 if i < 5 else -1) * abs(angle % (2 * 3.14159) - 3.14159) ** 0.5 )) draw.polygon(points, fill=255) # 应用遮罩 return Image.composite(image, Image.new('RGBA', image.size, (0, 0, 0, 0)), mask) @staticmethod def transform_image(image, x, y, width, height, angle): """应用缩放、旋转和位置变换""" import math # 确保宽度和高度大于0 width = max(1, width) height = max(1, height) # 缩放 scaled = image.resize((int(width), int(height)), Image.Resampling.LANCZOS) # 旋转 if angle != 0: # 创建一个更大的画布以容纳旋转后的图像 rotated_size = int(math.sqrt(width**2 + height**2)) * 2 temp_canvas = Image.new('RGBA', (rotated_size, rotated_size), (0, 0, 0, 0)) # 将缩放后的图像居中放置在临时画布上 temp_x = (rotated_size - scaled.width) // 2 temp_y = (rotated_size - scaled.height) // 2 temp_canvas.paste(scaled, (temp_x, temp_y)) # 旋转 rotated = temp_canvas.rotate(angle, expand=True, resample=Image.Resampling.BILINEAR) # 计算新的偏移量 offset_x = x - (rotated_size - scaled.width) // 2 offset_y = y - (rotated_size - scaled.height) // 2 return rotated, (offset_x, offset_y) else: # 如果没有旋转,直接返回缩放后的图像和位置 return scaled, (x, y) @staticmethod def perspective_transform(image, vertices, output_size): """应用透视变换,将图像映射到指定的四边形顶点""" try: import numpy as np import cv2 # 获取图像尺寸 img_width, img_height = image.size # 源坐标:图像的四个角 src_points = np.float32([ [0, 0], [img_width, 0], [0, img_height], [img_width, img_height] ]) # 目标坐标:指定的四个顶点 # 确保顶点坐标是列表而不是元组 dst_points = np.float32([list(v) for v in vertices]) # 计算透视变换矩阵 matrix = cv2.getPerspectiveTransform(src_points, dst_points) # 计算输出图像的尺寸 output_width, output_height = output_size # 应用透视变换 result = cv2.warpPerspective( np.array(image), matrix, (output_width, output_height), borderMode=cv2.BORDER_CONSTANT, borderValue=(0, 0, 0, 0) # 透明背景 ) # 转换回PIL图像 return Image.fromarray(result) except ImportError: # 如果没有安装opencv,返回原始图像 return image @staticmethod def transform_with_vertices(image, vertices): """根据顶点坐标对图像进行变形""" try: import numpy as np import cv2 # 获取图像尺寸 img_width, img_height = image.size # 源坐标:图像的四个角 src_points = np.float32([ [0, 0], [img_width, 0], [0, img_height], [img_width, img_height] ]) # 目标坐标:指定的四个顶点 dst_points = np.float32([list(v) for v in vertices]) # 计算包围盒 min_x = min(v[0] for v in dst_points) min_y = min(v[1] for v in dst_points) max_x = max(v[0] for v in dst_points) max_y = max(v[1] for v in dst_points) # 调整目标坐标为相对于包围盒的坐标 adjusted_dst_points = dst_points - np.array([min_x, min_y]) # 计算输出尺寸 output_width = int(max_x - min_x) output_height = int(max_y - min_y) # 确保输出尺寸大于0 output_width = max(1, output_width) output_height = max(1, output_height) # 计算透视变换矩阵 matrix = cv2.getPerspectiveTransform(src_points, adjusted_dst_points) # 应用透视变换,使用透明背景 result = cv2.warpPerspective( np.array(image), matrix, (output_width, output_height), borderMode=cv2.BORDER_CONSTANT, borderValue=(0, 0, 0, 0) # 透明背景而不是黑色背景 ) # 转换回PIL图像 if len(result.shape) == 3 and result.shape[2] == 4: # 如果有alpha通道 return Image.fromarray(result), (min_x, min_y) else: # 如果没有alpha通道 return Image.fromarray(result).convert('RGBA'), (min_x, min_y) except ImportError: # 如果没有安装opencv,使用简单的变换 min_x = min(v[0] for v in vertices) min_y = min(v[1] for v in vertices) # 确保返回的图像具有RGBA模式 if image.mode != 'RGBA': image = image.convert('RGBA') return image, (min_x, min_y) @staticmethod def save_image(image, file_path, format='png', quality=95, keep_exif=False): """保存图像到文件""" try: # 如果是JPEG格式,转换为RGB if format.lower() == 'jpeg': image = image.convert('RGB') # 保存图像 image.save( file_path, format=format.upper(), quality=quality, exif=image.info.get('exif') if keep_exif else None ) return True except Exception as e: print(f"保存图像失败: {e}") return False