海报库弄完了
This commit is contained in:
parent
6704dd58e7
commit
27c2170239
@ -71,7 +71,7 @@ class PosterConfigManager:
|
||||
"id": template_id,
|
||||
"name": template_info.get("name", template_id),
|
||||
"description": template_info.get("description", ""),
|
||||
"size": template_info.get("size", [900, 1200]),
|
||||
"size": template_info.get("size", [1350, 1800]),
|
||||
"required_fields": template_info.get("required_fields", []),
|
||||
"optional_fields": template_info.get("optional_fields", [])
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@ templates:
|
||||
vibrant:
|
||||
name: "Vibrant活力模板"
|
||||
description: "适合旅游景点、娱乐活动的活力海报模板"
|
||||
size: [900, 1200]
|
||||
size: [1350, 1800]
|
||||
required_fields: ["title", "slogan", "price", "ticket_type", "content_items"]
|
||||
optional_fields: ["remarks", "tag", "pagination"]
|
||||
prompt_key: "vibrant"
|
||||
|
||||
@ -26,7 +26,7 @@ class TemplateInfo(BaseModel):
|
||||
"id": "vibrant",
|
||||
"name": "Vibrant活力模板",
|
||||
"description": "适合旅游景点、娱乐活动的活力海报模板",
|
||||
"size": [900, 1200],
|
||||
"size": [1350, 1800],
|
||||
"required_fields": ["title", "slogan", "price", "ticket_type", "content_items"],
|
||||
"optional_fields": ["remarks", "tag", "pagination"]
|
||||
}
|
||||
@ -189,7 +189,7 @@ class PosterGenerationResponse(BaseAPIResponse):
|
||||
"metadata": {
|
||||
"image_used": "/data/images/ocean_park.jpg",
|
||||
"generation_method": "ai_generated",
|
||||
"template_size": [900, 1200],
|
||||
"template_size": [1350, 1800],
|
||||
"processing_time": 3.2
|
||||
}
|
||||
}
|
||||
@ -243,7 +243,7 @@ class TemplateListResponse(BaseAPIResponse):
|
||||
"id": "vibrant",
|
||||
"name": "Vibrant活力模板",
|
||||
"description": "适合旅游景点、娱乐活动的活力海报模板",
|
||||
"size": [900, 1200],
|
||||
"size": [1350, 1800],
|
||||
"required_fields": ["title", "slogan", "price"],
|
||||
"optional_fields": ["remarks", "tag"]
|
||||
}
|
||||
|
||||
Binary file not shown.
@ -232,7 +232,7 @@ class PosterService:
|
||||
# # 3. 图片解码
|
||||
images = None
|
||||
# 获取模板的默认尺寸,如果获取不到则使用标准尺寸
|
||||
template_size = getattr(template_handler, 'size', (900, 1200))
|
||||
template_size = getattr(template_handler, 'size', (1350, 1800))
|
||||
|
||||
if images_base64 and len(images_base64) > 0:
|
||||
try:
|
||||
@ -694,7 +694,7 @@ class PosterService:
|
||||
"""
|
||||
try:
|
||||
# 获取图像尺寸
|
||||
image_size = [images.width, images.height] if hasattr(images, 'width') else [900, 1200]
|
||||
image_size = [images.width, images.height] if hasattr(images, 'width') else [1350, 1800]
|
||||
|
||||
# 生成Fabric.js JSON数据
|
||||
fabric_json = self._generate_fabric_json(content, template_id, image_size, images)
|
||||
|
||||
@ -54,7 +54,7 @@ class UnifiedPosterService:
|
||||
id=template_id,
|
||||
name=template_info.get("name", template_id),
|
||||
description=template_info.get("description", ""),
|
||||
size=template_info.get("size", [900, 1200]),
|
||||
size=template_info.get("size", [1350, 1800]),
|
||||
required_fields=template_info.get("required_fields", []),
|
||||
optional_fields=template_info.get("optional_fields", [])
|
||||
)
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
"topic_index": 0,
|
||||
"variant_index": 1,
|
||||
"template": "business",
|
||||
"size": [900, 1200],
|
||||
"size": [1350, 1800],
|
||||
"generate_text": false,
|
||||
"params": {
|
||||
"top_image_path": "resource/data/images/hotel1.jpg",
|
||||
@ -52,7 +52,7 @@
|
||||
"topic_index": 1,
|
||||
"variant_index": 0,
|
||||
"template": "collage",
|
||||
"size": [900, 1200],
|
||||
"size": [1350, 1800],
|
||||
"generate_text": false,
|
||||
"params": {
|
||||
"image_paths": [
|
||||
|
||||
Binary file not shown.
@ -157,7 +157,7 @@ class AIModelConfig(BaseConfig):
|
||||
|
||||
class PosterConfig(BaseConfig):
|
||||
"""海报生成配置"""
|
||||
target_size: List[int] = Field(default_factory=lambda: [900, 1200])
|
||||
target_size: List[int] = Field(default_factory=lambda: [1350, 1800])
|
||||
additional_images_enabled: bool = True
|
||||
template_selection: str = "random" # random, business, vibrant, original
|
||||
available_templates: List[str] = Field(default_factory=lambda: ["original", "business", "vibrant"])
|
||||
|
||||
@ -1164,7 +1164,7 @@ class ImageProcessor:
|
||||
class BaseTemplate(ABC):
|
||||
"""海报模板基类"""
|
||||
|
||||
def __init__(self, size: Tuple[int, int] = (900, 1200)):
|
||||
def __init__(self, size: Tuple[int, int] = (1350, 1800)):
|
||||
"""
|
||||
初始化基础模板
|
||||
|
||||
@ -1562,7 +1562,7 @@ class BaseTemplate(ABC):
|
||||
class VibrantTemplate(BaseTemplate):
|
||||
"""活力风格海报模板(基于海洋模块)"""
|
||||
|
||||
def __init__(self, size: Tuple[int, int] = (900, 1200)):
|
||||
def __init__(self, size: Tuple[int, int] = (1350, 1800)):
|
||||
"""
|
||||
初始化活力模板
|
||||
|
||||
@ -2384,7 +2384,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
"备注和标签支持",
|
||||
"智能字体大小调整"
|
||||
],
|
||||
"recommended_size": (900, 1200),
|
||||
"recommended_size": (1350, 1800),
|
||||
"final_size": (1350, 1800),
|
||||
"style": "海洋活力风格",
|
||||
"theme_colors": list(self.ocean_config['ocean_colors'].keys()),
|
||||
@ -2458,12 +2458,12 @@ class VibrantTemplate(BaseTemplate):
|
||||
class BusinessTemplate(BaseTemplate):
|
||||
"""商务风格海报模板(基于酒店模块)"""
|
||||
|
||||
def __init__(self, size: Tuple[int, int] = (900, 1200)):
|
||||
def __init__(self, size: Tuple[int, int] = (1350, 1800)):
|
||||
"""
|
||||
初始化商务模板
|
||||
|
||||
Args:
|
||||
size: 海报尺寸,默认为酒店模块的尺寸 (900, 1200)
|
||||
size: 海报尺寸,默认为酒店模块的尺寸 (1350, 1800)
|
||||
"""
|
||||
super().__init__(size)
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -22,7 +22,7 @@ class BaseTemplate(ABC):
|
||||
定义了模板的通用接口和基础功能。
|
||||
"""
|
||||
|
||||
def __init__(self, size: Tuple[int, int] = (900, 1200), font_dir: str = "/root/autodl-tmp/TCC_RESTRUCT/assets/font"):
|
||||
def __init__(self, size: Tuple[int, int] = (1350, 1800), font_dir: str = "/root/autodl-tmp/TCC_RESTRUCT/assets/font"):
|
||||
self.width, self.height = size
|
||||
self.size = size
|
||||
self.image_processor = ImageProcessor()
|
||||
|
||||
@ -26,7 +26,7 @@ class BusinessTemplate(BaseTemplate):
|
||||
特点是上下图片融合,中间为信息展示区,整体风格沉稳、专业。
|
||||
"""
|
||||
|
||||
def __init__(self, size: Tuple[int, int] = (900, 1200)):
|
||||
def __init__(self, size: Tuple[int, int] = (1350, 1800)):
|
||||
super().__init__(size)
|
||||
self.config = {
|
||||
'total_parts': 4.0,
|
||||
|
||||
@ -22,7 +22,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
活力风格模板,适用于色彩鲜艳、内容丰富的场景,如旅游、美食分享。
|
||||
特点是底部有毛玻璃效果的文案区域。
|
||||
"""
|
||||
def __init__(self, size: Tuple[int, int] = (900, 1200)):
|
||||
def __init__(self, size: Tuple[int, int] = (1350, 1800)):
|
||||
super().__init__(size)
|
||||
self.config = {
|
||||
'colors': {
|
||||
@ -38,7 +38,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
'glass_effect': {
|
||||
'max_opacity': 240,
|
||||
'blur_radius': 22,
|
||||
'transition_height': 80,
|
||||
'transition_height': 120,
|
||||
'intensity_multiplier': 1.5
|
||||
},
|
||||
}
|
||||
@ -71,7 +71,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
**kwargs):
|
||||
"""
|
||||
生成Vibrant风格海报,支持统一渲染
|
||||
|
||||
|
||||
Args:
|
||||
images (List): 主图
|
||||
content (Optional[Dict[str, Any]]): 包含所有文本信息的字典
|
||||
@ -148,8 +148,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
canvas = self._create_composite_image(main_image, gradient_start, theme_color)
|
||||
canvas = self._render_texts(canvas, content, gradient_start)
|
||||
|
||||
final_image = canvas.resize((1350, 1800), Image.LANCZOS)
|
||||
return final_image
|
||||
return canvas
|
||||
|
||||
def _get_default_content(self) -> Dict[str, Any]:
|
||||
"""获取默认的测试内容"""
|
||||
@ -174,24 +173,24 @@ class VibrantTemplate(BaseTemplate):
|
||||
|
||||
def _estimate_content_height(self, content: Dict[str, Any]) -> int:
|
||||
"""预估内容高度"""
|
||||
standard_margin = 25
|
||||
title_height = 100
|
||||
subtitle_height = 80
|
||||
button_height = 40
|
||||
standard_margin = 38
|
||||
title_height = 150
|
||||
subtitle_height = 120
|
||||
button_height = 60
|
||||
content_items = content.get("content_items", [])
|
||||
content_line_height = 32
|
||||
content_line_height = 48
|
||||
content_list_height = len(content_items) * content_line_height
|
||||
price_height = 90
|
||||
ticket_height = 60
|
||||
price_height = 135
|
||||
ticket_height = 90
|
||||
remarks = content.get("remarks", [])
|
||||
if isinstance(remarks, str):
|
||||
remarks = [remarks]
|
||||
remarks_height = len(remarks) * 25 + 10
|
||||
footer_height = 40
|
||||
remarks_height = len(remarks) * 38 + 15
|
||||
footer_height = 60
|
||||
total_height = (
|
||||
20 + title_height + standard_margin + subtitle_height + standard_margin +
|
||||
button_height + 15 + content_list_height + price_height + ticket_height +
|
||||
remarks_height + footer_height + 30
|
||||
button_height + 23 + content_list_height + price_height + ticket_height +
|
||||
remarks_height + footer_height + 45
|
||||
)
|
||||
return total_height
|
||||
|
||||
@ -211,7 +210,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
except:
|
||||
continue
|
||||
if gradient_start is None:
|
||||
bottom_margin = 60
|
||||
bottom_margin = 90
|
||||
gradient_start = max(height - estimated_height - bottom_margin, height // 2)
|
||||
return gradient_start
|
||||
|
||||
@ -297,7 +296,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
return top_color, bottom_color
|
||||
|
||||
def _calculate_optimal_font_size_enhanced(self, text: str, target_width: int,
|
||||
max_size: int = 120, min_size: int = 10) -> Tuple[int, int]:
|
||||
max_size: int = 180, min_size: int = 15) -> Tuple[int, int]:
|
||||
"""
|
||||
计算文本的最佳字体大小,使其宽度接近目标宽度(增强版本,与demo一致)
|
||||
|
||||
@ -382,7 +381,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
|
||||
left_margin, right_margin = self._calculate_content_margins(content, width, center_x)
|
||||
|
||||
footer_y = height - 30
|
||||
footer_y = height - 45
|
||||
self._render_footer(draw, content, footer_y, left_margin, right_margin)
|
||||
|
||||
title_y = gradient_start + 40
|
||||
@ -404,7 +403,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
title_text = content.get("title", "")
|
||||
title_target_width = int(width * 0.95)
|
||||
title_size, title_width = self._calculate_optimal_font_size_enhanced(
|
||||
title_text, title_target_width, min_size=40, max_size=130
|
||||
title_text, title_target_width, min_size=60, max_size=195
|
||||
)
|
||||
title_x = center_x - title_width // 2
|
||||
|
||||
@ -412,26 +411,26 @@ class VibrantTemplate(BaseTemplate):
|
||||
slogan_text = content.get("slogan", "")
|
||||
subtitle_target_width = int(width * 0.9)
|
||||
subtitle_size, subtitle_width = self._calculate_optimal_font_size_enhanced(
|
||||
slogan_text, subtitle_target_width, max_size=50, min_size=20
|
||||
slogan_text, subtitle_target_width, max_size=75, min_size=30
|
||||
)
|
||||
subtitle_x = center_x - subtitle_width // 2
|
||||
|
||||
# 计算内容区域边距 - 减小额外的边距,让内容区域更宽
|
||||
padding = 20 # 从30减小到20
|
||||
padding = 30 # 从30减小到20,现在按1.5倍调整到30
|
||||
content_left_margin = min(title_x, subtitle_x) - padding
|
||||
content_right_margin = max(title_x + title_width, subtitle_x + subtitle_width) + padding
|
||||
|
||||
# 确保边距不超出合理范围,但允许更宽的内容区域
|
||||
content_left_margin = max(40, content_left_margin)
|
||||
content_right_margin = min(width - 40, content_right_margin)
|
||||
content_left_margin = max(60, content_left_margin)
|
||||
content_right_margin = min(width - 60, content_right_margin)
|
||||
|
||||
# 如果内容区域太窄,强制使用更宽的区域
|
||||
min_content_width = int(width * 0.75) # 至少使用75%的宽度
|
||||
current_width = content_right_margin - content_left_margin
|
||||
if current_width < min_content_width:
|
||||
extra_width = min_content_width - current_width
|
||||
content_left_margin = max(30, content_left_margin - extra_width // 2)
|
||||
content_right_margin = min(width - 30, content_right_margin + extra_width // 2)
|
||||
content_left_margin = max(45, content_left_margin - extra_width // 2)
|
||||
content_right_margin = min(width - 45, content_right_margin + extra_width // 2)
|
||||
|
||||
return content_left_margin, content_right_margin
|
||||
|
||||
@ -450,7 +449,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
title_text = content.get("title", "默认标题")
|
||||
title_target_width = int((right - left) * 0.98)
|
||||
title_size, title_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||
title_text, title_target_width, max_size=140, min_size=40
|
||||
title_text, title_target_width, max_size=210, min_size=60
|
||||
)
|
||||
title_font = self.text_renderer._load_default_font(title_size)
|
||||
|
||||
@ -463,14 +462,14 @@ class VibrantTemplate(BaseTemplate):
|
||||
subtitle_text = content.get("slogan", "")
|
||||
subtitle_target_width = int((right - left) * 0.95)
|
||||
subtitle_size, subtitle_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||
subtitle_text, subtitle_target_width, max_size=75, min_size=20
|
||||
subtitle_text, subtitle_target_width, max_size=113, min_size=30
|
||||
)
|
||||
subtitle_font = self.text_renderer._load_default_font(subtitle_size)
|
||||
|
||||
sub_text_w, sub_text_h = self.text_renderer.get_text_size(subtitle_text, subtitle_font)
|
||||
subtitle_x = center_x - sub_text_w // 2
|
||||
|
||||
title_spacing = 30
|
||||
title_spacing = 45
|
||||
|
||||
# 在标题下方添加装饰线
|
||||
line_y = y + text_h + 5
|
||||
@ -490,7 +489,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
button_text = content.get("content_button", "套餐内容")
|
||||
button_width, _ = self.text_renderer.get_text_size(button_text, button_font)
|
||||
button_width += 40
|
||||
button_height = 50
|
||||
button_height = 75
|
||||
self.text_renderer.draw_rounded_rectangle(draw=draw, position=(x, y), size=(button_width, button_height), radius=20, fill_color=(0, 140, 210, 180), outline_color=(255, 255, 255, 255), outline_width=1)
|
||||
draw.text((x + 20, y + (button_height - 30) // 2), button_text, font=button_font, fill=(255, 255, 255))
|
||||
|
||||
@ -513,7 +512,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
price_text = str(content.get('price', '')) # 确保价格是字符串类型
|
||||
price_target_width = int((right_margin - x) * 0.7)
|
||||
price_size, price_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||
price_text, price_target_width, max_size=120, min_size=40
|
||||
price_text, price_target_width, max_size=180, min_size=60
|
||||
)
|
||||
|
||||
price_font = self.text_renderer._load_default_font(price_size)
|
||||
@ -533,7 +532,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
ticket_text = content.get("ticket_type", "")
|
||||
ticket_target_width = int((right_margin - x) * 0.7)
|
||||
ticket_size, ticket_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||
ticket_text, ticket_target_width, max_size=60, min_size=30
|
||||
ticket_text, ticket_target_width, max_size=90, min_size=45
|
||||
)
|
||||
ticket_font = self.text_renderer._load_default_font(ticket_size)
|
||||
ticket_x = right_margin - ticket_actual_width
|
||||
@ -596,10 +595,10 @@ class VibrantTemplate(BaseTemplate):
|
||||
# === 第一步:创建与常规模式完全相同的最终结果作为Reference ===
|
||||
canvas = self._create_composite_image(main_image, gradient_start, theme_color)
|
||||
canvas = self._render_texts(canvas, content, gradient_start)
|
||||
final_canvas = canvas.resize((1350, 1800), Image.LANCZOS)
|
||||
final_canvas = canvas
|
||||
|
||||
# === 第二步:为PSD创建可编辑的图层版本 ===
|
||||
final_size = (1350, 1800)
|
||||
final_size = self.size
|
||||
psd = PSDImage.new("RGBA", final_size, color=(0, 0, 0, 0))
|
||||
logger.info(f"创建PSD文档,尺寸: {final_size}")
|
||||
|
||||
@ -690,7 +689,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
|
||||
# 4. 左栏内容层
|
||||
title_y = gradient_start + 40
|
||||
subtitle_height = 80 + 30 # 预估副标题高度
|
||||
subtitle_height = 120 + 30 # 预估副标题高度
|
||||
content_start_y = title_y + subtitle_height + 30
|
||||
content_area_width = right_margin - left_margin
|
||||
left_column_width = int(content_area_width * 0.5)
|
||||
@ -702,7 +701,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
layers["Right Content"] = self._create_right_column_layer(content, content_start_y, right_column_x, right_margin, canvas_size)
|
||||
|
||||
# 6. 页脚层
|
||||
footer_y = height - 30
|
||||
footer_y = height - 45
|
||||
layers["Footer Info"] = self._create_footer_layer(content, footer_y, left_margin, right_margin, canvas_size)
|
||||
|
||||
except Exception as e:
|
||||
@ -724,7 +723,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
left_margin, right_margin = self._calculate_content_margins(content, width, center_x)
|
||||
|
||||
# 页脚
|
||||
footer_y = height - 30
|
||||
footer_y = height - 45
|
||||
self._render_footer(draw, content, footer_y, left_margin, right_margin)
|
||||
|
||||
# 标题和副标题
|
||||
@ -759,7 +758,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
left_margin, right_margin = self._calculate_content_margins(content, width, center_x)
|
||||
|
||||
# 页脚
|
||||
footer_y = height - 30
|
||||
footer_y = height - 45
|
||||
self._render_footer(draw, content, footer_y, left_margin, right_margin)
|
||||
|
||||
# 标题和副标题
|
||||
@ -797,7 +796,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
title_text = content.get("title", "默认标题")
|
||||
title_target_width = int((right_margin - left_margin) * 0.98)
|
||||
title_size, title_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||
title_text, title_target_width, max_size=140, min_size=40
|
||||
title_text, title_target_width, max_size=210, min_size=60
|
||||
)
|
||||
try:
|
||||
title_font = ImageFont.truetype(self.chinese_font_path, title_size)
|
||||
@ -818,7 +817,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
if subtitle_text:
|
||||
subtitle_target_width = int((right_margin - left_margin) * 0.95)
|
||||
subtitle_size, subtitle_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||
subtitle_text, subtitle_target_width, max_size=75, min_size=20
|
||||
subtitle_text, subtitle_target_width, max_size=113, min_size=30
|
||||
)
|
||||
try:
|
||||
subtitle_font = ImageFont.truetype(self.chinese_font_path, subtitle_size)
|
||||
@ -836,7 +835,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
footer_canvas = Image.new('RGBA', canvas_size, (0, 0, 0, 0))
|
||||
footer_draw = ImageDraw.Draw(footer_canvas)
|
||||
|
||||
footer_y = height - 30
|
||||
footer_y = height - 45
|
||||
try:
|
||||
footer_font = ImageFont.truetype(self.chinese_font_path, 18)
|
||||
except:
|
||||
@ -899,7 +898,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
|
||||
# 使用指定的中文字体
|
||||
title_size, title_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||
title_text, title_target_width, max_size=140, min_size=40
|
||||
title_text, title_target_width, max_size=210, min_size=60
|
||||
)
|
||||
|
||||
try:
|
||||
@ -941,7 +940,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
|
||||
subtitle_target_width = int((right_margin - left_margin) * 0.95)
|
||||
subtitle_size, subtitle_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||
subtitle_text, subtitle_target_width, max_size=75, min_size=20
|
||||
subtitle_text, subtitle_target_width, max_size=113, min_size=30
|
||||
)
|
||||
|
||||
# 使用指定的中文字体
|
||||
@ -981,7 +980,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
title_text = content.get("title", "默认标题")
|
||||
title_target_width = int((right_margin - left_margin) * 0.98)
|
||||
title_size, title_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||
title_text, title_target_width, max_size=140, min_size=40
|
||||
title_text, title_target_width, max_size=210, min_size=60
|
||||
)
|
||||
|
||||
# 使用中文字体计算标题尺寸
|
||||
@ -1027,7 +1026,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
bbox = button_font.getbbox(button_text)
|
||||
button_text_width = bbox[2] - bbox[0]
|
||||
button_width = button_text_width + 40
|
||||
button_height = 50
|
||||
button_height = 75
|
||||
|
||||
# 绘制简单的矩形按钮
|
||||
draw.rounded_rectangle([x, y, x + button_width, y + button_height],
|
||||
@ -1070,7 +1069,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
if price_text:
|
||||
price_target_width = int((right_margin - x) * 0.7)
|
||||
price_size, price_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||
price_text, price_target_width, max_size=120, min_size=40
|
||||
price_text, price_target_width, max_size=180, min_size=60
|
||||
)
|
||||
|
||||
try:
|
||||
@ -1101,7 +1100,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
if ticket_text:
|
||||
ticket_target_width = int((right_margin - x) * 0.7)
|
||||
ticket_size, ticket_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||
ticket_text, ticket_target_width, max_size=60, min_size=30
|
||||
ticket_text, ticket_target_width, max_size=90, min_size=45
|
||||
)
|
||||
|
||||
try:
|
||||
@ -1228,7 +1227,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
title_text = content.get("title", "")
|
||||
title_target_width = int((right_margin - left_margin) * 0.98)
|
||||
title_size, title_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||
title_text, title_target_width, max_size=140, min_size=40
|
||||
title_text, title_target_width, max_size=210, min_size=60
|
||||
)
|
||||
title_x = center_x - title_actual_width // 2
|
||||
title_y = gradient_start + 40
|
||||
@ -1237,7 +1236,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
subtitle_text = content.get("slogan", "")
|
||||
subtitle_target_width = int((right_margin - left_margin) * 0.95)
|
||||
subtitle_size, subtitle_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||
subtitle_text, subtitle_target_width, max_size=75, min_size=20
|
||||
subtitle_text, subtitle_target_width, max_size=113, min_size=30
|
||||
)
|
||||
subtitle_x = center_x - subtitle_actual_width // 2
|
||||
subtitle_y = title_y + 100 + 30 # 标题高度 + 间距
|
||||
@ -1252,14 +1251,14 @@ class VibrantTemplate(BaseTemplate):
|
||||
price_text = str(content.get('price', ''))
|
||||
price_target_width = int((right_margin - right_column_x) * 0.7)
|
||||
price_size, price_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||
price_text, price_target_width, max_size=120, min_size=40
|
||||
price_text, price_target_width, max_size=180, min_size=60
|
||||
)
|
||||
|
||||
# 票种布局计算
|
||||
ticket_text = content.get("ticket_type", "")
|
||||
ticket_target_width = int((right_margin - right_column_x) * 0.7)
|
||||
ticket_size, ticket_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||
ticket_text, ticket_target_width, max_size=60, min_size=30
|
||||
ticket_text, ticket_target_width, max_size=90, min_size=45
|
||||
)
|
||||
|
||||
layout_params = {
|
||||
@ -1315,10 +1314,9 @@ class VibrantTemplate(BaseTemplate):
|
||||
"""使用统一布局参数渲染PNG"""
|
||||
canvas = self._create_composite_image(main_image, gradient_start, theme_color)
|
||||
canvas = self._render_texts(canvas, content, gradient_start)
|
||||
final_image = canvas.resize((1350, 1800), Image.LANCZOS)
|
||||
|
||||
return {
|
||||
"image": final_image,
|
||||
"image": canvas,
|
||||
"layout_params": layout_params,
|
||||
"format": "png"
|
||||
}
|
||||
@ -1327,7 +1325,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
theme_color: Optional[str], gradient_start: int,
|
||||
layout_params: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""使用统一布局参数渲染JSON"""
|
||||
final_width, final_height = 1350, 1800
|
||||
final_width, final_height = self.size
|
||||
fabric_objects = []
|
||||
|
||||
# 1. 背景图片
|
||||
@ -1454,7 +1452,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
"height": 100,
|
||||
"text": layout_params["title_text"],
|
||||
"fontSize": layout_params["title_size"],
|
||||
"fontFamily": "Arial Black",
|
||||
"fontFamily": "兰亭粗黑简",
|
||||
"fontWeight": "bold",
|
||||
"fill": "white",
|
||||
"textAlign": "center",
|
||||
@ -1479,7 +1477,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
"height": 80,
|
||||
"text": layout_params["subtitle_text"],
|
||||
"fontSize": layout_params["subtitle_size"],
|
||||
"fontFamily": "Arial",
|
||||
"fontFamily": "兰亭粗黑简",
|
||||
"fill": "white",
|
||||
"textAlign": "center",
|
||||
"lineHeight": 1.3,
|
||||
@ -1511,9 +1509,9 @@ class VibrantTemplate(BaseTemplate):
|
||||
"y2": 0,
|
||||
"stroke": "rgba(215, 215, 215, 0.3)",
|
||||
"strokeWidth": 3,
|
||||
"opacity": 1,
|
||||
"angle": 0,
|
||||
"scaleX": 1,
|
||||
"opacity": 1,
|
||||
"angle": 0,
|
||||
"scaleX": 1,
|
||||
"scaleY": 1,
|
||||
"selectable": False,
|
||||
"evented": False
|
||||
@ -1533,33 +1531,33 @@ class VibrantTemplate(BaseTemplate):
|
||||
"strokeWidth": 1,
|
||||
"rx": 20,
|
||||
"ry": 20,
|
||||
"opacity": 1,
|
||||
"angle": 0,
|
||||
"scaleX": 1,
|
||||
"opacity": 1,
|
||||
"angle": 0,
|
||||
"scaleX": 1,
|
||||
"scaleY": 1,
|
||||
"selectable": False,
|
||||
"evented": False
|
||||
}
|
||||
}
|
||||
text_objects.append(button_obj)
|
||||
|
||||
button_text_obj = {
|
||||
"type": "textbox",
|
||||
"type": "textbox",
|
||||
"left": layout_params["left_margin"] + 20,
|
||||
"top": layout_params["content_start_y"] + 10,
|
||||
"width": 160,
|
||||
"height": 30,
|
||||
"height": 30,
|
||||
"text": button_text,
|
||||
"fontSize": 30,
|
||||
"fontFamily": "Arial",
|
||||
"fontSize": 45,
|
||||
"fontFamily": "兰亭粗黑简",
|
||||
"fontWeight": "bold",
|
||||
"fill": "white",
|
||||
"textAlign": "center",
|
||||
"textAlign": "center",
|
||||
"lineHeight": 1.0,
|
||||
"opacity": 1,
|
||||
"angle": 0,
|
||||
"scaleX": 1,
|
||||
"scaleY": 1
|
||||
}
|
||||
"opacity": 1,
|
||||
"angle": 0,
|
||||
"scaleX": 1,
|
||||
"scaleY": 1
|
||||
}
|
||||
text_objects.append(button_text_obj)
|
||||
|
||||
# 5. 左栏内容列表
|
||||
@ -1573,14 +1571,14 @@ class VibrantTemplate(BaseTemplate):
|
||||
"width": layout_params["left_column_width"],
|
||||
"height": 35,
|
||||
"text": f"• {item}",
|
||||
"fontSize": 28,
|
||||
"fontFamily": "Arial",
|
||||
"fontSize": 42,
|
||||
"fontFamily": "兰亭粗黑简",
|
||||
"fill": "white",
|
||||
"textAlign": "left",
|
||||
"lineHeight": 1.2,
|
||||
"opacity": 1,
|
||||
"angle": 0,
|
||||
"scaleX": 1,
|
||||
"opacity": 1,
|
||||
"angle": 0,
|
||||
"scaleX": 1,
|
||||
"scaleY": 1
|
||||
}
|
||||
text_objects.append(item_obj)
|
||||
@ -1596,15 +1594,15 @@ class VibrantTemplate(BaseTemplate):
|
||||
"height": 80,
|
||||
"text": layout_params["price_text"],
|
||||
"fontSize": layout_params["price_size"],
|
||||
"fontFamily": "Arial Black",
|
||||
"fontFamily": "兰亭粗黑简",
|
||||
"fontWeight": "bold",
|
||||
"fill": "white",
|
||||
"textAlign": "right",
|
||||
"lineHeight": 1.0,
|
||||
"opacity": 1,
|
||||
"angle": 0,
|
||||
"scaleX": 1,
|
||||
"scaleY": 1,
|
||||
"angle": 0,
|
||||
"scaleX": 1,
|
||||
"scaleY": 1,
|
||||
"shadow": {
|
||||
"color": "rgba(0, 0, 0, 0.5)",
|
||||
"blur": 2,
|
||||
@ -1616,20 +1614,20 @@ class VibrantTemplate(BaseTemplate):
|
||||
|
||||
# CNY起后缀
|
||||
suffix_obj = {
|
||||
"type": "textbox",
|
||||
"type": "textbox",
|
||||
"left": price_x + layout_params["price_width"],
|
||||
"top": layout_params["content_start_y"] + 50,
|
||||
"width": 60,
|
||||
"height": 30,
|
||||
"text": "CNY起",
|
||||
"fontSize": int(layout_params["price_size"] * 0.3),
|
||||
"fontFamily": "Arial",
|
||||
"fill": "white",
|
||||
"textAlign": "left",
|
||||
"fontFamily": "兰亭粗黑简",
|
||||
"fill": "white",
|
||||
"textAlign": "left",
|
||||
"lineHeight": 1.0,
|
||||
"opacity": 1,
|
||||
"angle": 0,
|
||||
"scaleX": 1,
|
||||
"opacity": 1,
|
||||
"angle": 0,
|
||||
"scaleX": 1,
|
||||
"scaleY": 1
|
||||
}
|
||||
text_objects.append(suffix_obj)
|
||||
@ -1646,10 +1644,10 @@ class VibrantTemplate(BaseTemplate):
|
||||
"y2": 0,
|
||||
"stroke": "rgba(255, 255, 255, 0.3)",
|
||||
"strokeWidth": 2,
|
||||
"opacity": 1,
|
||||
"angle": 0,
|
||||
"scaleX": 1,
|
||||
"scaleY": 1,
|
||||
"opacity": 1,
|
||||
"angle": 0,
|
||||
"scaleX": 1,
|
||||
"scaleY": 1,
|
||||
"selectable": False,
|
||||
"evented": False
|
||||
}
|
||||
@ -1659,22 +1657,22 @@ class VibrantTemplate(BaseTemplate):
|
||||
if layout_params["ticket_text"]:
|
||||
ticket_x = layout_params["right_margin"] - layout_params["ticket_width"]
|
||||
ticket_obj = {
|
||||
"type": "textbox",
|
||||
"type": "textbox",
|
||||
"left": ticket_x,
|
||||
"top": layout_params["content_start_y"] + 115,
|
||||
"width": layout_params["ticket_width"],
|
||||
"height": 60,
|
||||
"text": layout_params["ticket_text"],
|
||||
"fontSize": layout_params["ticket_size"],
|
||||
"fontFamily": "Arial",
|
||||
"fontFamily": "兰亭粗黑简",
|
||||
"fontWeight": "bold",
|
||||
"fill": "white",
|
||||
"fill": "white",
|
||||
"textAlign": "right",
|
||||
"lineHeight": 1.0,
|
||||
"opacity": 1,
|
||||
"angle": 0,
|
||||
"scaleX": 1,
|
||||
"scaleY": 1,
|
||||
"opacity": 1,
|
||||
"angle": 0,
|
||||
"scaleX": 1,
|
||||
"scaleY": 1,
|
||||
"shadow": {
|
||||
"color": "rgba(0, 0, 0, 0.5)",
|
||||
"blur": 2,
|
||||
@ -1696,8 +1694,8 @@ class VibrantTemplate(BaseTemplate):
|
||||
"width": layout_params["right_margin"] - layout_params["right_column_x"],
|
||||
"height": 20,
|
||||
"text": remark,
|
||||
"fontSize": 16,
|
||||
"fontFamily": "Arial",
|
||||
"fontSize": 24,
|
||||
"fontFamily": "兰亭粗黑简",
|
||||
"fill": "rgba(255, 255, 255, 0.8)",
|
||||
"textAlign": "right",
|
||||
"lineHeight": 1.0,
|
||||
@ -1712,44 +1710,44 @@ class VibrantTemplate(BaseTemplate):
|
||||
footer_y = layout_params["footer_y"]
|
||||
if tag := content.get("tag"):
|
||||
tag_obj = {
|
||||
"type": "textbox",
|
||||
"type": "textbox",
|
||||
"left": layout_params["left_margin"],
|
||||
"top": footer_y,
|
||||
"width": 200,
|
||||
"height": 25,
|
||||
"text": tag,
|
||||
"fontSize": 18,
|
||||
"fontFamily": "Arial",
|
||||
"fontSize": 27,
|
||||
"fontFamily": "兰亭粗黑简",
|
||||
"fill": "white",
|
||||
"textAlign": "left",
|
||||
"textAlign": "left",
|
||||
"lineHeight": 1.0,
|
||||
"opacity": 1,
|
||||
"angle": 0,
|
||||
"scaleX": 1,
|
||||
"opacity": 1,
|
||||
"angle": 0,
|
||||
"scaleX": 1,
|
||||
"scaleY": 1
|
||||
}
|
||||
text_objects.append(tag_obj)
|
||||
|
||||
if pagination := content.get("pagination"):
|
||||
pagination_obj = {
|
||||
"type": "textbox",
|
||||
"type": "textbox",
|
||||
"left": layout_params["right_margin"] - 200,
|
||||
"top": footer_y,
|
||||
"top": footer_y,
|
||||
"width": 200,
|
||||
"height": 25,
|
||||
"text": pagination,
|
||||
"fontSize": 18,
|
||||
"fontFamily": "Arial",
|
||||
"fontSize": 27,
|
||||
"fontFamily": "兰亭粗黑简",
|
||||
"fill": "white",
|
||||
"textAlign": "right",
|
||||
"lineHeight": 1.0,
|
||||
"opacity": 1,
|
||||
"angle": 0,
|
||||
"scaleX": 1,
|
||||
"opacity": 1,
|
||||
"angle": 0,
|
||||
"scaleX": 1,
|
||||
"scaleY": 1
|
||||
}
|
||||
text_objects.append(pagination_obj)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"创建精确文本对象失败: {e}")
|
||||
|
||||
|
||||
@ -1040,7 +1040,7 @@ class ImageCollageCreator:
|
||||
参数:
|
||||
input_dir: 输入图片目录路径
|
||||
style: 拼贴样式,如不指定则随机选择
|
||||
target_size: 目标尺寸,默认为(900, 1200)
|
||||
target_size: 目标尺寸,默认为(1350, 1800)
|
||||
|
||||
返回:
|
||||
tuple: (拼贴图, 选择的图片名称列表),如果创建失败则返回(None, [])
|
||||
@ -1049,7 +1049,7 @@ class ImageCollageCreator:
|
||||
try:
|
||||
# 设置默认尺寸为3:4比例
|
||||
if target_size is None:
|
||||
target_size = (900, 1200) # 3:4比例
|
||||
target_size = (1350, 1800) # 3:4比例
|
||||
|
||||
# 如果没有指定样式,随机选择一种
|
||||
if style is None or style not in self.collage_styles:
|
||||
@ -1914,7 +1914,7 @@ class PosterNotesCreator:
|
||||
np.random.seed(seed)
|
||||
|
||||
# 调用core.simple_collage模块处理图像
|
||||
target_size = (900, 1200) # 3:4比例
|
||||
target_size = (1350, 1800) # 3:4比例
|
||||
collage_images, used_image_filenames = process_collage(
|
||||
temp_dir,
|
||||
style=style,
|
||||
@ -2070,7 +2070,7 @@ class PosterNotesCreator:
|
||||
np.random.seed(seed)
|
||||
|
||||
# 调用core.simple_collage模块处理图像
|
||||
target_size = (900, 1200) # 3:4比例
|
||||
target_size = (1350, 1800) # 3:4比例
|
||||
collage_images, used_image_filenames = process_collage(
|
||||
temp_dir,
|
||||
style=style,
|
||||
@ -3274,13 +3274,13 @@ def select_additional_images(
|
||||
collage_style
|
||||
)
|
||||
|
||||
def process_directory(directory_path, style=None, target_size=(900, 1200), output_count=1):
|
||||
def process_directory(directory_path, style=None, target_size=(1350, 1800), output_count=1):
|
||||
"""
|
||||
处理指定目录中的图片,创建指定数量的拼贴图。
|
||||
|
||||
参数:
|
||||
directory_path: 包含图片的目录路径
|
||||
target_size: 拼贴图目标尺寸,默认为 (900, 1200)
|
||||
target_size: 拼贴图目标尺寸,默认为 (1350, 1800)
|
||||
output_count: 需要生成的拼贴图数量,默认为 1
|
||||
|
||||
返回:
|
||||
@ -3371,7 +3371,7 @@ def main():
|
||||
logging.info("方法 1: 使用 process_directory 函数生成拼贴图...")
|
||||
collages_1, used_image_names_1 = process_directory(
|
||||
directory_path=test_directory,
|
||||
target_size=(900, 1200), # 默认 3:4 比例
|
||||
target_size=(1350, 1800), # 默认 3:4 比例
|
||||
output_count=2 # 创建 2 张不同的拼贴图
|
||||
)
|
||||
|
||||
|
||||
BIN
test_size_adjustment_result.png
Normal file
BIN
test_size_adjustment_result.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 205 KiB |
Binary file not shown.
@ -185,7 +185,7 @@ class ImageProcessor:
|
||||
return image
|
||||
|
||||
@staticmethod
|
||||
def create_collage_from_paths(image_paths: List[str], target_size: Tuple[int, int] = (900, 1200)) -> Optional[Image.Image]:
|
||||
def create_collage_from_paths(image_paths: List[str], target_size: Tuple[int, int] = (1350, 1800)) -> Optional[Image.Image]:
|
||||
"""
|
||||
从图像路径列表创建拼图
|
||||
|
||||
@ -257,7 +257,7 @@ class ImageProcessor:
|
||||
|
||||
@staticmethod
|
||||
def process_images_for_poster(image_paths: List[str],
|
||||
target_size: Tuple[int, int] = (900, 1200),
|
||||
target_size: Tuple[int, int] = (1350, 1800),
|
||||
create_collage: bool = False) -> Dict[str, Any]:
|
||||
"""
|
||||
处理图像用于海报生成
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user