import cv2 import numpy as np import glob from PIL import Image import os import random import time from .image_process import images_process def load_images(image_path, stickers_path): """ 加载矩形图片和贴纸图片。 :param image_path: 矩形图片路径 :param stickers_path: 贴纸图片文件夹路径 :return: 矩形图片(RGBA格式)、贴纸列表 """ shape_img = Image.open(image_path).convert("RGBA") sticker_files = glob.glob(os.path.join(stickers_path, '*')) stickers = [Image.open(sticker).convert("RGBA") for sticker in sticker_files] return shape_img, stickers def detect_rectangle(shape_image): """ 检测矩形,并返回左上坐标和宽高。 :param shape_image: 输入图片 :return: 左上坐标和宽高 """ # 转换为灰度图并二值化 gray_img = np.array(shape_image.convert("L")) _, binary = cv2.threshold(gray_img, 127, 255, cv2.THRESH_BINARY) # 查找最大轮廓 contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnt = max(contours, key=cv2.contourArea) # 获取矩形参数 x, y, w, h = cv2.boundingRect(cnt) print("矩形参数 x, y, w, h:", x, y, w, h) return (x, y), (w, h) def generate_scaled_rectangles(location, size, n_scales = 4): """ 生成n次同心缩放后的矩形轮廓。 :param location: 左上角坐标 :param size: 宽高 :param n_scales: 缩放次数 :param scale_factor: 缩放因子 :return: 缩放后的矩形列表 """ scaled_shapes = [] x, y = location w, h = size print("原始矩形大小:", w, h) for i in range(n_scales): scaled_w = int(w * (1-i/n_scales)) scaled_h = int(h * (1-i/n_scales)) # 计算新的左上角位置,保持居中 new_x = x + (w - scaled_w) // 2 new_y = y + (h - scaled_h) // 2 scaled_shapes.append(((new_x, new_y), (scaled_w, scaled_h))) return scaled_shapes def generate_grid(location, size, cell_size, sticker_size): """ 在矩形中生成规则网格。 :param location: 左上角坐标 :param size: 矩形宽高 :param cell_size: 网格单元大小 :return: 网格点位置列表 """ x, y = location w, h = size rows = h // cell_size cols = w // cell_size # 新增,需测试 row_height = (h - sticker_size)//(rows-1) col_width = (w - sticker_size)//(cols-1) grid_positions = [(x + j * col_width, y + i * row_height) for i in range(rows) for j in range(cols)] # 新增,需测试 ''' grid_positions = [(x + j * cell_size, y + i * cell_size) for i in range(rows) for j in range(cols)] ''' print('网格数量:', len(grid_positions)) return grid_positions def transform_sticker(sticker, sticker_size, angle_open=True): """ 对贴纸进行缩放和旋转变换。 :param sticker: 贴纸图片 :param sticker_size: 贴纸大小 :return: 变换后的贴纸 """ # 缩放贴纸 long_side = max(sticker.size) scale_factor = (sticker_size / long_side)* random.uniform(0.95, 1.05) new_size = tuple(int(dim * scale_factor) for dim in sticker.size) sticker = sticker.resize(new_size, Image.Resampling.LANCZOS) # 随机旋转贴纸 if angle_open: angle = random.uniform(-10, 10) sticker = sticker.rotate(angle, expand=True) return sticker def place_stickers(scaled_shapes, stickers, main_sticker, transparent_bg, sticker_size, main_sticker_size, cell_size_factor): """ 在矩形内放置贴纸。 :param scaled_shapes: 缩放后的矩形列表 :param stickers: 贴纸列表 :param main_sticker: 主贴纸 :param transparent_bg: 透明背景 :param sticker_size: 贴纸大小 :param main_sticker_size: 主贴纸大小 :param location: 矩形左上角坐标 :param size: 矩形宽高 :return: 贴纸拼贴结果 """ ''' right_boundary = location[0] + size[0] bottom_boundary = location[1] + size[1] cell_size = int(sticker_size * cell_size_factor) ''' cell_size = int(sticker_size * cell_size_factor) # 贴纸计数器 count = random.randint(0, len(stickers)-1) # 逐层放置贴纸 for i, shape in enumerate(scaled_shapes): # 获取该层的边界值 left_boundary = shape[0][0] top_boundary = shape[0][1] right_boundary = left_boundary + shape[1][0] bottom_boundary = top_boundary + shape[1][1] # 生成网格 grid_positions = generate_grid(shape[0], shape[1], cell_size, sticker_size) # 在网格点放置贴纸 for j, pos in enumerate(grid_positions): sticker = stickers[count % len(stickers)] # transformed = transform_sticker(sticker, sticker_size) # 贴纸左右偏移 x_offset = random.randint(-5, 5) # 处理左侧边界对齐 if pos[0] == left_boundary: angle_open = False transformed = transform_sticker(sticker, sticker_size, angle_open) if bottom_boundary - pos[1] < int(2*cell_size): pos = (pos[0], bottom_boundary - transformed.size[1]) # 处理右侧边界对齐 elif right_boundary - pos[0] < int(2*cell_size): angle_open = False transformed = transform_sticker(sticker, sticker_size, angle_open) pos = (right_boundary - transformed.size[0], pos[1]) if bottom_boundary - pos[1] < int(2*cell_size): pos = (right_boundary - transformed.size[0], bottom_boundary - transformed.size[1]) # 处理顶部边界对齐 elif pos[1] == top_boundary: angle_open = False transformed = transform_sticker(sticker, sticker_size, angle_open) # 处理底部边界对齐 elif bottom_boundary - pos[1] < int(2*cell_size): angle_open = False transformed = transform_sticker(sticker, sticker_size, angle_open) pos = (pos[0], bottom_boundary - transformed.size[1]) else: transformed = transform_sticker(sticker, sticker_size) # 贴纸放置 transparent_bg.paste(transformed, (pos[0] + x_offset, pos[1]), transformed) count += 1 # 放置顶层主贴纸 main_sticker = transform_sticker(random.choice(main_sticker), main_sticker_size) w, h = transparent_bg.size pos_x = w//2 - main_sticker.size[0]//2 pos_y = h//2 - main_sticker.size[1]//2 transparent_bg.paste(main_sticker, (pos_x, pos_y), main_sticker) return transparent_bg def rectangle_contour_main(image_path, stickers_path, main_sticker_path, n_scales=4, sticker_size=220, main_sticker_size=380, cell_size_factor=0.45, crop_image=False, padding=0): """ 主函数。 :param image_path: 矩形图片路径 :param stickers_path: 贴纸文件夹路径 :param main_sticker_path: 主贴纸路径 :param n_scales: 缩放次数 :param sticker_size: 贴纸大小 :param main_sticker_size: 主贴纸大小 :param crop_image: 是否裁剪图片 :param padding: 裁剪时额外添加的边距 """ # 加载图片和贴纸 # shape_image, stickers = load_images(image_path, stickers_path) # _, main_sticker = load_images(image_path, main_sticker_path) shape_image = images_process(image_path) shape_image = shape_image[0] stickers = images_process(stickers_path, crop_image=crop_image, padding=padding) main_sticker = images_process(main_sticker_path, crop_image=crop_image, padding=padding) # 检测矩形并生成缩放序列 location, size = detect_rectangle(shape_image) scaled_shapes = generate_scaled_rectangles(location, size, n_scales) # 创建透明背景并放置贴纸 transparent_bg = Image.new("RGBA", shape_image.size, (0, 0, 0, 0)) result = place_stickers(scaled_shapes, stickers, main_sticker, transparent_bg, sticker_size, main_sticker_size, cell_size_factor) return result if __name__ == "__main__": # 开始时间 start_time = time.time() image_path = "images/rectangle.png" stickers_path = "images/baby_bear/all" main_sticker_path = "images/baby_bear/top" output_path = "images/output/output103.png" result_image = rectangle_contour_main(image_path, stickers_path, main_sticker_path, n_scales=4, sticker_size=280, main_sticker_size=450, cell_size_factor=0.45) result_image.save(output_path, format='PNG') print("处理完成") # 结束时间 end_time = time.time() print(f"总时间:{end_time - start_time:.2f}秒")