增加了图片输出的控制

This commit is contained in:
jinye_huang 2025-04-26 13:45:47 +08:00
parent fee00d1935
commit 218a91659b
8 changed files with 149 additions and 108 deletions

View File

@ -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
if len(images) == 0:
logging.error("No images could be loaded. Cannot create collage.")
return None, []
logging.info(f"Successfully loaded {len(images)} images for collage.")
# 确保图片数量满足要求,不足则复制已有图片
while len(images) < num_images:
images.append(random.choice(images).copy())
logging.debug(f"Duplicated an image to reach required count of {num_images}")
# 创建空白画布 (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
# 应用所选样式
logging.info(f"Applying style '{style}'...")
result_collage = None
# 根据样式创建拼图
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) # 自定义尺寸

View File

@ -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,6 +147,10 @@ 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)
# 输出使用的图片名称
if i < len(used_image_names):
print(f"拼贴图已保存: {output_path},使用图片: {used_image_names[i]}")
else:
print(f"拼贴图已保存: {output_path}")
# 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)
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}")
print(f"风格拼贴图已保存: {output_path},使用图片: {selected_images}")
else:
print(f"创建 {style} 风格失败: 未返回有效拼贴图")
except Exception as e:
print(f"创建 {style} 风格失败: {e}")

View File

@ -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,

View File

@ -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}")

View File

@ -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: