diff --git a/core/__pycache__/simple_collage.cpython-312.pyc b/core/__pycache__/simple_collage.cpython-312.pyc index f10b63d..82c1e67 100644 Binary files a/core/__pycache__/simple_collage.cpython-312.pyc and b/core/__pycache__/simple_collage.cpython-312.pyc differ diff --git a/core/simple_collage.py b/core/simple_collage.py index 4dd4193..4892f7d 100644 --- a/core/simple_collage.py +++ b/core/simple_collage.py @@ -134,7 +134,16 @@ class ImageCollageCreator: return img def create_collage_with_style(self, input_dir, style=None, target_size=None): - """创建指定样式的拼接画布""" + """创建指定样式的拼接画布 + + 参数: + input_dir: 输入图片目录路径 + style: 拼贴样式,如不指定则随机选择 + target_size: 目标尺寸,默认为(900, 1200) + + 返回: + tuple: (拼贴图, 选择的图片名称列表),如果创建失败则返回(None, []) + """ logging.info(f"--- Starting Collage Creation for Directory: {input_dir} ---") # Start Log try: # 设置默认尺寸为3:4比例 @@ -149,7 +158,7 @@ class ImageCollageCreator: # 检查目录是否存在 if not os.path.exists(input_dir): logging.error(f"Input directory does not exist: {input_dir}") - return None + return None, [] # 支持的图片格式 image_extensions = ('.jpg', '.jpeg', '.png', '.bmp') @@ -160,7 +169,7 @@ class ImageCollageCreator: logging.info(f"Files found in directory: {all_files}") except Exception as e: logging.exception(f"Error listing directory {input_dir}: {e}") - return None + return None, [] # 过滤图片文件 all_images_names = [f for f in all_files @@ -169,7 +178,7 @@ class ImageCollageCreator: if not all_images_names: logging.warning(f"No valid image files found in directory: {input_dir}") - return None # Return None if no images found + return None, [] # Return None if no images found # 根据不同样式,确定需要的图片数量 # ... (logic for num_images based on style) ... @@ -193,75 +202,72 @@ class ImageCollageCreator: selected_images_names = (all_images_names * (num_images // len(all_images_names) + 1))[:num_images] else: logging.error("Cannot select images, none were found.") # Should not happen due to earlier check - return None + return None, [] else: # 随机选择指定数量的图片 selected_images_names = random.sample(all_images_names, num_images) - logging.info(f"Selected image files for collage: {selected_images_names}") + # 记录并输出被选择的图片名称 + logging.info(f"Selected images for collage: {selected_images_names}") + print(f"为拼贴图选择的图片: {selected_images_names}") - # 加载图片 + # 加载选中的图片 images = [] - loaded_image_paths = set() for img_name in selected_images_names: - img_path = os.path.join(input_dir, img_name) + image_path = os.path.join(input_dir, img_name) try: - img = Image.open(img_path).convert('RGBA') + img = Image.open(image_path).convert('RGBA') images.append(img) - loaded_image_paths.add(img_path) - logging.info(f"Successfully loaded image: {img_path}") + logging.debug(f"Successfully loaded image: {img_name}") except Exception as e: - logging.error(f"Failed to load image {img_path}: {e}", exc_info=True) # Log exception info - # Optionally: try to replace failed image (or just log and continue) - # For simplicity now, just log and continue; the check below handles insufficient images. + logging.exception(f"Error loading image {img_name}: {e}") - # 再次检查实际加载成功的图片数量 - if len(images) < num_images: - logging.error(f"Needed {num_images} images, but only successfully loaded {len(images)}. Cannot create collage.") - # Log which images failed if possible (from error logs above) - return None - - logging.info(f"Successfully loaded {len(images)} images for collage.") - - # 创建空白画布 (moved after image loading success check) - # collage_image = Image.new('RGBA', target_size, (0, 0, 0, 0)) # This line seems unused as styles create their own canvas + if len(images) == 0: + logging.error("No images could be loaded. Cannot create collage.") + return None, [] - # 应用所选样式 - logging.info(f"Applying style '{style}'...") - result_collage = None + # 确保图片数量满足要求,不足则复制已有图片 + while len(images) < num_images: + images.append(random.choice(images).copy()) + logging.debug(f"Duplicated an image to reach required count of {num_images}") + + # 根据样式创建拼图 + collage = None if style == "grid_2x2": - result_collage = self._create_grid_2x2_collage(images, target_size) - # ... (elif for all other styles) ... + collage = self._create_grid_2x2_collage(images, target_size) elif style == "asymmetric": - result_collage = self._create_asymmetric_collage(images, target_size) + collage = self._create_asymmetric_collage(images, target_size) elif style == "filmstrip": - result_collage = self._create_filmstrip_collage(images, target_size) - # elif style == "circles": - # result_collage = self._create_circles_collage(images, target_size) - elif style == "polaroid": - result_collage = self._create_polaroid_collage(images, target_size) + collage = self._create_filmstrip_collage(images, target_size) + elif style == "circles": + collage = self._create_circles_collage(images, target_size) elif style == "overlap": - result_collage = self._create_overlap_collage(images, target_size) + collage = self._create_overlap_collage(images, target_size) + elif style == "polaroid": + collage = self._create_polaroid_collage(images, target_size) elif style == "mosaic": - result_collage = self._create_mosaic_collage(images, target_size) + collage = self._create_mosaic_collage(images, target_size) elif style == "fullscreen": - result_collage = self._create_fullscreen_collage(images, target_size) + collage = self._create_fullscreen_collage(images, target_size) elif style == "vertical_stack": - result_collage = self._create_vertical_stack_collage(images, target_size) - else: - logging.warning(f"Unknown style '{style}', defaulting to grid_2x2.") - result_collage = self._create_grid_2x2_collage(images, target_size) + collage = self._create_vertical_stack_collage(images, target_size) - if result_collage is None: - logging.error(f"Collage creation failed during style application ('{style}').") - return None + if collage: + logging.info(f"Successfully created collage with style: {style}") else: - logging.info(f"--- Collage Creation Successful for Directory: {input_dir} ---") - return result_collage # Return the created collage image + logging.error(f"Failed to create collage with style: {style}") + return None, [] + + # 清理内存中的原始图像 + for img in images: + if hasattr(img, 'close'): + img.close() + return collage, selected_images_names except Exception as e: - logging.exception(f"An unexpected error occurred during collage creation for {input_dir}: {e}") # Log full traceback - return None + logging.exception(f"Error in create_collage_with_style: {e}") + traceback.print_exc() + return None, [] def _create_grid_2x2_collage(self, images, target_size): """创建2x2网格拼贴画""" @@ -503,15 +509,16 @@ class ImageCollageCreator: return collage def _create_overlap_collage(self, images, target_size): - """创建无缝重叠风格拼贴画""" + """创建无重叠风格拼贴画(不允许图片重叠)""" collage = Image.new('RGBA', target_size, (255, 255, 255, 255)) width, height = target_size - # 计算每个图像确切填充一个区域的大小 - img_width = width // 2 - img_height = height // 2 + # 为了避免重叠,计算每个图像区域的大小 + grid_size = 2 # 2x2网格 + img_width = width // grid_size + img_height = height // grid_size - # 网格位置 - 完全填充画布 + # 定义网格位置 - 确保无重叠 positions = [ (0, 0), # 左上 (img_width, 0), # 右上 @@ -527,20 +534,19 @@ class ImageCollageCreator: img = self.resize_and_crop(img, (img_width, img_height)) # 应用效果 img = self.apply_image_effect(img) - # 不添加边框 # 粘贴到拼贴画 collage.paste(img, position) - print(f"添加无缝拼贴画块 {i+1} 到位置: {position}") + print(f"添加无重叠拼贴画块 {i+1} 到位置: {position},尺寸: {img_width}x{img_height}") - print(f"无缝拼贴画创建成功,尺寸: {target_size}") + print(f"无重叠拼贴画创建成功,尺寸: {target_size}") return collage def _create_mosaic_collage(self, images, target_size): - """创建马赛克风格拼贴画(需要9张图片)""" + """创建马赛克风格拼贴画(需要9张图片,无重叠)""" collage = Image.new('RGBA', target_size, (255, 255, 255, 255)) width, height = target_size - # 创建3x3网格 + # 创建3x3网格,确保无重叠 grid_width = width // 3 grid_height = height // 3 @@ -550,17 +556,6 @@ class ImageCollageCreator: for col in range(3): positions.append((col * grid_width, row * grid_height)) - # 定义不同的效果 - effects = ["none", "grayscale", "sepia", "vintage", "high_contrast", - "color_boost", "vibrant", "warm", "none"] - - # 保证所有效果都能使用 - if len(effects) > len(images): - effects = effects[:len(images)] - - # 随机打乱效果 - random.shuffle(effects) - # 将图像粘贴到马赛克位置 for i, position in enumerate(positions): if i < len(images): @@ -568,17 +563,16 @@ class ImageCollageCreator: # 调整大小 img = self.resize_and_crop(img, (grid_width, grid_height)) # 应用效果 - img = self.apply_image_effect(img, effects[i % len(effects)]) - # 不添加边框 + img = self.apply_image_effect(img) # 粘贴到拼贴画 collage.paste(img, position) - print(f"添加马赛克拼贴画块 {i+1} 到位置: {position}") + print(f"添加马赛克拼贴画块 {i+1} 到位置: {position},尺寸: {grid_width}x{grid_height}") - print(f"无缝马赛克拼贴画创建成功,尺寸: {target_size}") + print(f"无重叠马赛克拼贴画创建成功,尺寸: {target_size}") return collage def _create_fullscreen_collage(self, images, target_size): - """创建全覆盖拼图样式(完全填满画布,无空白)""" + """创建全覆盖拼图样式(完全填满画布,无空白,无重叠)""" width, height = target_size collage = Image.new('RGBA', target_size, (255, 255, 255, 255)) # 白色背景 @@ -589,7 +583,7 @@ class ImageCollageCreator: else: return None - # 定义区域划分 - 按照完全填满画布设计 + # 定义区域划分 - 按照完全填满画布设计,确保无重叠 regions = [ # 左列 - 上中下三块 (0, 0, width//2, height//3), # 左上 @@ -602,7 +596,7 @@ class ImageCollageCreator: (width//2, height*2//3, width, height) # 右下 ] - # 添加图片到各个区域,确保完全覆盖 + # 添加图片到各个区域,确保完全覆盖无重叠 for i, (x1, y1, x2, y2) in enumerate(regions): if i < len(images): img = images[i].copy() @@ -612,18 +606,14 @@ class ImageCollageCreator: region_height = y2 - y1 img = self.resize_and_crop(img, (region_width, region_height)) - # 应用轻微的图像效果,确保视觉统一性 + # 应用轻微的图像效果 img = self.apply_image_effect(img) - # 不添加边框,实现无缝拼接 - # 粘贴到画布上 collage.paste(img, (x1, y1)) - print(f"添加全屏拼图块 {i+1} 到位置: ({x1}, {y1}, {x2}, {y2})") + print(f"添加全屏拼图块 {i+1} 到位置: ({x1}, {y1}, {x2}, {y2}),尺寸: {region_width}x{region_height}") - # 不添加分隔线,保持无缝 - - print(f"无缝全覆盖拼图创建成功,尺寸: {target_size}") + print(f"无重叠全覆盖拼图创建成功,尺寸: {target_size}") return collage def _create_vertical_stack_collage(self, images, target_size): @@ -708,32 +698,58 @@ def process_directory(directory_path, style=None, target_size=(900, 1200), outpu output_count: 需要生成的拼贴图数量,默认为 1 返回: - list: 生成的拼贴图列表(PIL.Image 对象);如果生成失败,返回空列表 + tuple: (拼贴图列表, 使用的图片名称列表的列表),如果生成失败,返回 ([], []) + 拼贴图列表是PIL.Image对象列表 + 图片名称列表是一个列表的列表,每个子列表包含一张拼贴图使用的图片文件名 """ logging.info(f"处理目录中的图片并创建 {output_count} 个拼贴图: {directory_path}") # 创建 ImageCollageCreator 实例 collage_creator = ImageCollageCreator() collage_images = [] + used_image_names = [] # 存储每个拼贴图使用的图片文件名 # 检查目录是否存在 if not os.path.exists(directory_path): logging.error(f"目录不存在: {directory_path}") - return [] + return [], [] + + # 支持的图片格式 + image_extensions = ('.jpg', '.jpeg', '.png', '.bmp') + + # 获取目录中的所有有效图片文件 + try: + all_files = os.listdir(directory_path) + all_images_names = [f for f in all_files + if f.lower().endswith(image_extensions) and os.path.isfile(os.path.join(directory_path, f))] + + if not all_images_names: + logging.error(f"目录中没有有效的图片文件: {directory_path}") + return [], [] + + logging.info(f"目录中找到 {len(all_images_names)} 个有效图片文件") + except Exception as e: + logging.exception(f"列出目录内容时出错: {e}") + return [], [] # 尝试创建请求数量的拼贴图 for i in range(output_count): try: - # 随机选择一个样式(由 create_collage_with_style 内部实现) - # 传入 None 作为 style 参数,让函数内部随机选择 - collage = collage_creator.create_collage_with_style( + # 创建拼贴图,使用指定样式 + collage, selected_images_names = collage_creator.create_collage_with_style( directory_path, - style=style, # 让方法内部随机选择样式 + style=style, target_size=target_size ) if collage: collage_images.append(collage) + + # 从输出日志中解析出使用的图片名称 + # 由于我们修改了create_collage_with_style来打印选择的图片 + # 可能需要进一步修改为直接返回选择的图片 + used_image_names.append(selected_images_names) + logging.info(f"成功创建拼贴图 {i+1}/{output_count}") else: logging.error(f"无法创建拼贴图 {i+1}/{output_count}") @@ -741,7 +757,7 @@ def process_directory(directory_path, style=None, target_size=(900, 1200), outpu logging.exception(f"创建拼贴图 {i+1}/{output_count} 时发生异常: {e}") logging.info(f"已处理目录 {directory_path},成功创建 {len(collage_images)}/{output_count} 个拼贴图") - return collage_images + return collage_images, used_image_names def find_main_subject(image): # ... (keep the existing implementation) ... @@ -767,7 +783,7 @@ def main(): # 方法 1: 使用 process_directory 函数 (推荐用于外部调用) logging.info("方法 1: 使用 process_directory 函数生成拼贴图...") - collages_1 = process_directory( + collages_1, used_image_names_1 = process_directory( directory_path=test_directory, target_size=(900, 1200), # 默认 3:4 比例 output_count=2 # 创建 2 张不同的拼贴图 @@ -793,7 +809,7 @@ def main(): for style in styles_to_try: logging.info(f"尝试使用样式: {style}") - collage = creator.create_collage_with_style( + collage, selected_images_names = creator.create_collage_with_style( input_dir=test_directory, style=style, target_size=(800, 1000) # 自定义尺寸 diff --git a/examples/test_simple_collage.py b/examples/test_simple_collage.py index be29fed..79b9bde 100644 --- a/examples/test_simple_collage.py +++ b/examples/test_simple_collage.py @@ -133,7 +133,7 @@ def main(): # 1. 处理整个目录,获取多个拼贴图 print("方法1: 使用process_directory批量生成") - collages = simple_collage.process_directory( + collages, used_image_names = simple_collage.process_directory( input_dir, target_size=target_size, output_count=args.count @@ -147,7 +147,11 @@ def main(): for i, collage in enumerate(collages): output_path = os.path.join(args.output_dir, f"collage_auto_{i+1}.png") collage.save(output_path) - print(f"拼贴图已保存: {output_path}") + # 输出使用的图片名称 + if i < len(used_image_names): + print(f"拼贴图已保存: {output_path},使用图片: {used_image_names[i]}") + else: + print(f"拼贴图已保存: {output_path}") # 2. 使用不同风格创建拼贴图 print("\n方法2: 测试不同风格") @@ -156,10 +160,13 @@ def main(): for style in styles: print(f"创建 {style} 风格拼贴图...") try: - collage = collage_creator.create_collage_with_style(input_dir, style, target_size) - output_path = os.path.join(args.output_dir, f"collage_style_{style}.png") - collage.save(output_path) - print(f"风格拼贴图已保存: {output_path}") + collage, selected_images = collage_creator.create_collage_with_style(input_dir, style, target_size) + if collage: + output_path = os.path.join(args.output_dir, f"collage_style_{style}.png") + collage.save(output_path) + print(f"风格拼贴图已保存: {output_path},使用图片: {selected_images}") + else: + print(f"创建 {style} 风格失败: 未返回有效拼贴图") except Exception as e: print(f"创建 {style} 风格失败: {e}") diff --git a/poster_gen_config.json b/poster_gen_config.json index 3606e97..28f70c4 100644 --- a/poster_gen_config.json +++ b/poster_gen_config.json @@ -1,6 +1,6 @@ { "date": "4月30日, 4月28日, 5月1日", - "num": 10, + "num": 1, "variants": 3, "topic_temperature": 0.2, "topic_top_p": 0.3, diff --git a/utils/__pycache__/output_handler.cpython-312.pyc b/utils/__pycache__/output_handler.cpython-312.pyc index ebc3422..71038d0 100644 Binary files a/utils/__pycache__/output_handler.cpython-312.pyc and b/utils/__pycache__/output_handler.cpython-312.pyc differ diff --git a/utils/__pycache__/tweet_generator.cpython-312.pyc b/utils/__pycache__/tweet_generator.cpython-312.pyc index 3f6fbfa..885d944 100644 Binary files a/utils/__pycache__/tweet_generator.cpython-312.pyc and b/utils/__pycache__/tweet_generator.cpython-312.pyc differ diff --git a/utils/output_handler.py b/utils/output_handler.py index 4e5ece1..60c4049 100644 --- a/utils/output_handler.py +++ b/utils/output_handler.py @@ -22,13 +22,14 @@ class OutputHandler(ABC): pass @abstractmethod - def handle_generated_image(self, run_id: str, topic_index: int, variant_index: int, image_type: str, image_data, output_filename: str): + def handle_generated_image(self, run_id: str, topic_index: int, variant_index: int, image_type: str, image_data, output_filename: str, metadata: dict = None): """Handles a generated image (collage or final poster). Args: image_type: Either 'collage' or 'poster'. image_data: The image data (e.g., PIL Image object or bytes). output_filename: The desired filename for the output (e.g., 'poster.jpg'). + metadata: Optional dictionary with additional metadata about the image (e.g., used image files). """ pass @@ -119,7 +120,7 @@ class FileSystemOutputHandler(OutputHandler): except Exception as save_err: logging.error(f"Failed to save complete poster configurations for topic {topic_index} to {config_path}: {save_err}") - def handle_generated_image(self, run_id: str, topic_index: int, variant_index: int, image_type: str, image_data, output_filename: str): + def handle_generated_image(self, run_id: str, topic_index: int, variant_index: int, image_type: str, image_data, output_filename: str, metadata: dict = None): """Saves a generated image (PIL Image) to the appropriate variant subdirectory.""" subdir = None if image_type == 'collage': @@ -134,9 +135,22 @@ class FileSystemOutputHandler(OutputHandler): save_path = os.path.join(target_dir, output_filename) try: + # 保存图片 # Assuming image_data is a PIL Image object based on posterGen/simple_collage image_data.save(save_path) logging.info(f"Saved {image_type} image to: {save_path}") + + # 保存元数据(如果有) + if metadata: + metadata_filename = os.path.splitext(output_filename)[0] + "_metadata.json" + metadata_path = os.path.join(target_dir, metadata_filename) + try: + with open(metadata_path, 'w', encoding='utf-8') as f: + json.dump(metadata, f, ensure_ascii=False, indent=4) + logging.info(f"Saved {image_type} metadata to: {metadata_path}") + except Exception as me: + logging.error(f"Failed to save {image_type} metadata to {metadata_path}: {me}") + except Exception as e: logging.exception(f"Failed to save {image_type} image to {save_path}: {e}") diff --git a/utils/tweet_generator.py b/utils/tweet_generator.py index 7f807f8..4471985 100644 --- a/utils/tweet_generator.py +++ b/utils/tweet_generator.py @@ -627,7 +627,7 @@ def generate_posters_for_topic(topic_item: dict, # --- Image Collage --- logging.info(f"Generating collage from: {input_img_dir_path}") - collage_images = core_simple_collage.process_directory( + collage_images, used_image_filenames = core_simple_collage.process_directory( input_img_dir_path, style=collage_style, target_size=poster_target_size, @@ -638,14 +638,17 @@ def generate_posters_for_topic(topic_item: dict, logging.warning(f"Warning: Failed to generate collage image for Variant {variant_index}. Skipping poster.") continue collage_img = collage_images[0] # 获取第一个 PIL Image - logging.info(f"Collage image generated successfully (in memory).") + used_image_files = used_image_filenames[0] if used_image_filenames else [] # 获取使用的图片文件名 + logging.info(f"Collage image generated successfully (in memory). Used images: {used_image_files}") + print(f"拼贴图使用的图片文件: {used_image_files}") - # --- 使用 Handler 保存 Collage 图片 --- + # --- 使用 Handler 保存 Collage 图片和使用的图片文件信息 --- output_handler.handle_generated_image( run_id, topic_index, variant_index, image_type='collage', image_data=collage_img, - output_filename='collage.png' # 或者其他期望的文件名 + output_filename='collage.png', # 或者其他期望的文件名 + metadata={'used_images': used_image_files} # 添加图片文件信息到元数据 ) # --- 结束保存 Collage --- @@ -677,7 +680,8 @@ def generate_posters_for_topic(topic_item: dict, run_id, topic_index, variant_index, image_type='poster', image_data=poster_img, - output_filename=output_poster_filename # 使用参数中的文件名 + output_filename=output_poster_filename, # 使用参数中的文件名 + metadata={'used_collage': True, 'collage_images': used_image_files} ) # --- 结束保存 Poster --- else: