Mikiko Bazeley
commited on
Commit
·
57eccf2
1
Parent(s):
c6c35c1
Refactored and removed controlnet
Browse files- .DS_Store +0 -0
- app.py +56 -10
- pages/1_Generate_Holiday_Postcard.py +107 -180
- pages/2_Generate_Holiday_Borders.py +0 -371
- pages/2_Generate_Multiple_Holiday_Borders.py +249 -0
- pages/3_Customize_Holiday_Borders.py +170 -274
- pages/4_Morph_Holiday_Card.py +0 -268
- test_endpoint.py +4 -3
- utils/__init__.py +0 -0
- utils/configuration.py +185 -0
- utils/helper_utilities.py +281 -0
.DS_Store
ADDED
Binary file (6.15 kB). View file
|
|
app.py
CHANGED
@@ -12,7 +12,6 @@ card_with_message_1_image = Image.open("img/card_with_message_1.png")
|
|
12 |
card_with_message_2_image = Image.open("img/card_with_message_2.png")
|
13 |
|
14 |
|
15 |
-
|
16 |
# Fireworks Logo at the top
|
17 |
st.image(logo_image)
|
18 |
|
@@ -23,8 +22,41 @@ st.title("🎨 Flux Holiday Magic: Custom Card Creator")
|
|
23 |
# Description using Streamlit text and markdown
|
24 |
st.markdown("""
|
25 |
Welcome to the ultimate holiday card design experience! 🎄❄️
|
26 |
-
|
27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
""")
|
29 |
|
30 |
# Features list with emojis
|
@@ -36,7 +68,7 @@ st.markdown("""
|
|
36 |
- **Advanced Customization**: Take control of parameters like ControlNet conditioning, guidance scale, inference steps, and more for tailored results.
|
37 |
- **Save & Share**: Download your final creations or save them for later in a handy ZIP file.
|
38 |
|
39 |
-
And be sure to share with us on social!
|
40 |
|
41 |
![Holiday Vibes](https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExMXo5Y3RhOGNzaTkzZHFmandsZmF0MW9jNHN4M3VjMjdpOGhyYnRxMSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/637gfK5GJdv552H9ab/giphy.gif)
|
42 |
""")
|
@@ -46,9 +78,17 @@ st.markdown("""
|
|
46 |
Get started now and add a magical touch to your holiday greetings! 🎅🌟
|
47 |
""")
|
48 |
|
|
|
49 |
st.divider()
|
50 |
-
|
51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
col1, col2 = st.columns(2)
|
53 |
|
54 |
with col1:
|
@@ -59,13 +99,19 @@ with col2:
|
|
59 |
st.image(parameters_2_image)
|
60 |
st.image(parameters_4_image)
|
61 |
|
|
|
|
|
62 |
|
|
|
|
|
|
|
|
|
|
|
63 |
|
64 |
-
|
65 |
-
|
66 |
-
st.markdown(""" ![Special message, so special](https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExenVkdmZzZTdxNjIxcDk2b2NrdXUxbDBneTNqdHUyamFxNTQzd2dleiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/qDJSK8oxEkBV0XJE7B/giphy.gif)""")
|
67 |
col3, col4 = st.columns(2)
|
68 |
with col3:
|
69 |
st.image(card_with_message_1_image)
|
70 |
with col4:
|
71 |
-
st.image(card_with_message_2_image)
|
|
|
12 |
card_with_message_2_image = Image.open("img/card_with_message_2.png")
|
13 |
|
14 |
|
|
|
15 |
# Fireworks Logo at the top
|
16 |
st.image(logo_image)
|
17 |
|
|
|
22 |
# Description using Streamlit text and markdown
|
23 |
st.markdown("""
|
24 |
Welcome to the ultimate holiday card design experience! 🎄❄️
|
25 |
+
|
26 |
+
Powered by **Flux models** through **Fireworks API**, this app lets you effortlessly create stunning, personalized holiday cards with just a few clicks. Here’s what each page allows you to do:
|
27 |
+
""")
|
28 |
+
|
29 |
+
# Overview of the pages with descriptions
|
30 |
+
st.markdown("""
|
31 |
+
### 1. **Generate Holiday Postcard: 🎇 FLUX-tastic Holiday Postcard Generator 🎨**
|
32 |
+
Get ready to make your holiday greetings pop with a personalized, AI-generated postcard! 🎅🎨 No more boring, store-bought cards. Customize a dazzling holiday scene with your own snazzy message and festive designs, brought to life by **FLUX models**.
|
33 |
+
|
34 |
+
**How it works:**
|
35 |
+
- Choose a holiday-themed prompt or write your own 🎉
|
36 |
+
- Select a FLUX model to bring your vision to life ✨
|
37 |
+
- Customize with fonts, text backgrounds, and colors 🎨
|
38 |
+
- Generate and share your festive masterpiece with friends and family 📬
|
39 |
+
|
40 |
+
### 2. **Generate Multiple Holiday Borders: 🎨 Holiday Multi-Card Generator 🎨**
|
41 |
+
This page is your first stop for crafting a holiday card with multiple festive border designs. 🌟 Play around with different styles, prompts, and parameters to design the perfect card border before adding your message in the next step.
|
42 |
+
|
43 |
+
**How it works:**
|
44 |
+
- 🖼️ **Upload Your Image**: Choose the image that will be the centerpiece of your card.
|
45 |
+
- ✂️ **Crop & Adjust**: Fine-tune the crop to highlight the most important parts of your image.
|
46 |
+
- 💡 **Choose Your Style**: Select from festive borders or input your own custom prompt.
|
47 |
+
- ⚙️ **Fine-Tune**: Experiment with guidance scales, seeds, inference steps, and more for the perfect aesthetic.
|
48 |
+
- 👀 **Preview & Download**: See your designs, tweak them, and download them as a ZIP file.
|
49 |
+
|
50 |
+
After perfecting your border, head over to Part B to add a personal message!
|
51 |
+
|
52 |
+
### 3. **Customize Holiday Borders: 🎄 Holiday Card Customizer 🎅**
|
53 |
+
✨ Welcome to the final part of your holiday card creation journey! It’s time to add a festive border and a personal message to your card.
|
54 |
+
|
55 |
+
**How it works:**
|
56 |
+
- 🎁 **Upload Your Image**: Select a photo or design to be the star of your holiday card.
|
57 |
+
- ✨ **Design Your Holiday Border**: Choose from seasonal prompts or create a unique custom border.
|
58 |
+
- 💌 **Add Your Personal Message**: Write a heartfelt, funny, or warm message to spread holiday cheer.
|
59 |
+
- 📦 **Download Your Finished Card**: Ready to send to friends and family as a gift of joy! 🎉
|
60 |
""")
|
61 |
|
62 |
# Features list with emojis
|
|
|
68 |
- **Advanced Customization**: Take control of parameters like ControlNet conditioning, guidance scale, inference steps, and more for tailored results.
|
69 |
- **Save & Share**: Download your final creations or save them for later in a handy ZIP file.
|
70 |
|
71 |
+
And be sure to share with us on social media!
|
72 |
|
73 |
![Holiday Vibes](https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExMXo5Y3RhOGNzaTkzZHFmandsZmF0MW9jNHN4M3VjMjdpOGhyYnRxMSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/637gfK5GJdv552H9ab/giphy.gif)
|
74 |
""")
|
|
|
78 |
Get started now and add a magical touch to your holiday greetings! 🎅🌟
|
79 |
""")
|
80 |
|
81 |
+
# Divider for sections
|
82 |
st.divider()
|
83 |
+
|
84 |
+
# Subheader for experimenting with Flux models
|
85 |
+
st.subheader("🎨 Experiment with Flux to design your perfect holiday card border 🎄")
|
86 |
+
st.markdown("""
|
87 |
+
Use different parameters and prompts to customize your holiday card design.
|
88 |
+
Play around with borders, colors, and styles to find the festive magic you're looking for. 🎁
|
89 |
+
|
90 |
+
![For science!](https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExbGwwdGh4czc3bnU3czNzaWRhaXl4YXYzdm53Nmw1OXhpd2E4emFuYSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/RdJIM4Uesg37eN1ZNL/giphy.gif)
|
91 |
+
""")
|
92 |
col1, col2 = st.columns(2)
|
93 |
|
94 |
with col1:
|
|
|
99 |
st.image(parameters_2_image)
|
100 |
st.image(parameters_4_image)
|
101 |
|
102 |
+
# Divider for sections
|
103 |
+
st.divider()
|
104 |
|
105 |
+
# Subheader for adding custom messages
|
106 |
+
st.subheader("💌 Customize Your Holiday Card with a Special Message 💌")
|
107 |
+
st.markdown("""
|
108 |
+
Add the final touch with a heartfelt, funny, or creative message!
|
109 |
+
Choose your font, color, and size to make your holiday message pop! 🎉
|
110 |
|
111 |
+
![Special message, so special](https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExenVkdmZzZTdxNjIxcDk2b2NrdXUxbDBneTNqdHUyamFxNTQzd2dleiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/qDJSK8oxEkBV0XJE7B/giphy.gif)
|
112 |
+
""")
|
|
|
113 |
col3, col4 = st.columns(2)
|
114 |
with col3:
|
115 |
st.image(card_with_message_1_image)
|
116 |
with col4:
|
117 |
+
st.image(card_with_message_2_image)
|
pages/1_Generate_Holiday_Postcard.py
CHANGED
@@ -1,236 +1,153 @@
|
|
1 |
-
|
2 |
-
import requests
|
3 |
-
from dotenv import load_dotenv
|
4 |
-
from PIL import Image, ImageDraw, ImageFont
|
5 |
-
from io import BytesIO
|
6 |
-
import streamlit as st
|
7 |
-
import textwrap
|
8 |
-
from PIL import ImageColor
|
9 |
|
|
|
|
|
|
|
|
|
|
|
10 |
|
11 |
# Set page configuration
|
12 |
st.set_page_config(page_title="FLUX Image Generation Tool", page_icon="🎇")
|
13 |
|
14 |
-
#
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
load_dotenv(dotenv_path, override=True)
|
19 |
-
|
20 |
-
# Get the Fireworks API key from the .env file
|
21 |
-
fireworks_api_key = os.getenv("FIREWORKS_API_KEY")
|
22 |
-
|
23 |
-
if not fireworks_api_key:
|
24 |
-
st.error("API key not found. Make sure FIREWORKS_API_KEY is set in the .env file.")
|
25 |
-
|
26 |
-
# Define the fonts you want to allow
|
27 |
-
fonts = {
|
28 |
-
"DejaVu Sans Bold": "./fonts/dejavu-sans-bold.ttf",
|
29 |
-
"Covered By You": "./fonts/CoveredByYourGrace-Regular.ttf",
|
30 |
-
"Julee Regular": "./fonts/Julee-Regular.ttf",
|
31 |
-
"Kalam Regular": "./fonts/Kalam-Regular.ttf",
|
32 |
-
"Knewave Regular": "./fonts/Knewave-Regular.ttf",
|
33 |
-
"Sancreek Regular": "./fonts/Sancreek-Regular.ttf",
|
34 |
-
"Vast Shadow Regular": "./fonts/VastShadow-Regular.ttf",
|
35 |
-
}
|
36 |
-
|
37 |
-
# Holiday-themed prompts list
|
38 |
-
holiday_prompts = [
|
39 |
-
"A peaceful winter landscape covered in fresh snow, with delicate snowflakes drifting through the air and soft, frosty patterns along the edges, creating a serene holiday card",
|
40 |
-
"A cozy Christmas scene featuring a warmly lit cabin in the woods, surrounded by snow-covered trees and twinkling holiday lights, with a sky glowing softly in the background",
|
41 |
-
"A fireplace with stockings, garlands, and a glowing hearth, set in a cozy room decorated for the holidays, capturing the warmth and tranquility of the season",
|
42 |
-
"A colorful Hanukkah celebration with glowing menorahs, dreidels, and shimmering holiday lights illuminating the night, set against a rich, deep blue backdrop for a festive scene",
|
43 |
-
"A vibrant New Year's Eve cityscape with bright fireworks exploding in the sky, colorful confetti drifting through the air, and stars twinkling above the skyline, radiating excitement",
|
44 |
-
"A mystical Samhain forest scene, with pumpkins glowing softly among tall trees, and a full moon casting light over an ancient Celtic stone circle, evoking an air of magic and mystery",
|
45 |
-
"A lively Día de Muertos altar adorned with colorful calaveras, bright marigold flowers, and glowing candles, set against a traditional Mexican backdrop, celebrating the holiday’s joy",
|
46 |
-
"A wintery Yule forest scene under a starry sky, with snow-covered pine trees, holly bushes, and traditional Norse winter symbols decorating the landscape, capturing the warmth of the season",
|
47 |
-
"A bright and vibrant Diwali celebration with glowing diyas lining a courtyard, intricate rangoli patterns on the ground, and colorful fireworks lighting up the night sky",
|
48 |
-
"A serene Kwanzaa scene featuring a kinara with glowing candles, surrounded by decorations in red, black, and green, set in a peaceful and reflective atmosphere",
|
49 |
-
"A bountiful Thanksgiving table scene set outdoors, surrounded by autumn leaves, pumpkins, and a golden sunset, capturing the essence of gratitude and togetherness",
|
50 |
-
"A tranquil Winter Solstice night with a glowing full moon above, bare trees, stars twinkling in the sky, and a peaceful blanket of snow covering the quiet landscape",
|
51 |
-
"A festive St. Patrick's Day scene featuring a green countryside, with shamrocks covering the rolling hills, a rainbow stretching across the sky, and a pot of gold in the distance",
|
52 |
-
"A colorful Lunar New Year street scene with red lanterns hanging from the rooftops, dragon dancers parading down the street, and vibrant fireworks lighting up the night",
|
53 |
-
"A peaceful Easter garden filled with blooming spring flowers, pastel-colored eggs hidden among the grass, and soft sunlight streaming through the trees",
|
54 |
-
"A romantic Valentine's Day scene with glowing candles, heart-shaped decorations hanging from tree branches, and a soft pink and red sunset in the sky",
|
55 |
-
"A lively Holi celebration in an open field, with bursts of vibrant color powder filling the air, and people joyfully celebrating under a bright blue sky",
|
56 |
-
"A tranquil Ramadan evening scene with a crescent moon and stars hanging in the night sky, glowing lanterns decorating the streets, and peaceful reflections along a quiet river",
|
57 |
-
"A lively Independence Day celebration with colorful fireworks lighting up the night sky, flags waving in the breeze, and a peaceful lakeside reflecting the celebration"
|
58 |
-
]
|
59 |
-
|
60 |
-
# Example messages corresponding to each holiday prompt
|
61 |
-
example_messages = [
|
62 |
-
"Wishing you peace and serenity this winter season. May the snow bring you joy and tranquility!",
|
63 |
-
"May your Christmas be as cozy and warm as a cabin in the woods, filled with holiday lights and love.",
|
64 |
-
"Warm wishes for the holidays! May your hearth and home be filled with love and joy this season.",
|
65 |
-
"Happy Hanukkah! May the light of the menorah shine brightly and bring joy to your heart.",
|
66 |
-
"Wishing you a vibrant New Year's Eve filled with excitement, fireworks, and joy for the year ahead!",
|
67 |
-
"May the magic and mystery of Samhain fill your spirit with wonder. Have a mystical holiday!",
|
68 |
-
"Celebrating the joy of Día de Muertos! May the memories of your loved ones bring peace and happiness.",
|
69 |
-
"Wishing you a Yule season filled with the warmth of the Norse tradition and the beauty of snowy forests.",
|
70 |
-
"Happy Diwali! May the lights of the diyas and the colors of rangoli bring you prosperity and joy.",
|
71 |
-
"Wishing you a reflective and peaceful Kwanzaa. May your home be filled with love, light, and unity.",
|
72 |
-
"Happy Thanksgiving! May your table be filled with gratitude, and your heart with the warmth of togetherness.",
|
73 |
-
"Wishing you a peaceful Winter Solstice night under the full moon. May the season bring you reflection and rest.",
|
74 |
-
"May the luck of the Irish be with you! Wishing you a joyful St. Patrick's Day filled with rainbows and gold.",
|
75 |
-
"Happy Lunar New Year! May the dragon dancers bring joy, and the fireworks light up a prosperous new year.",
|
76 |
-
"Wishing you a peaceful Easter surrounded by blooming flowers and the warmth of springtime sunshine.",
|
77 |
-
"Happy Valentine's Day! May your day be filled with love, glowing candles, and heartwarming moments.",
|
78 |
-
"Wishing you a joyful Holi! May the colors of the festival fill your life with vibrancy and happiness.",
|
79 |
-
"Ramadan Mubarak! May the crescent moon bring you peace, and the lanterns light up your spiritual path.",
|
80 |
-
"Happy Independence Day! May the fireworks light up the sky, and may freedom continue to inspire us all."
|
81 |
-
]
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
# Function to make requests to the FLUX models
|
86 |
-
def generate_flux_image(model_path, prompt, steps):
|
87 |
-
url = f"https://api.fireworks.ai/inference/v1/workflows/accounts/fireworks/models/{model_path}/text_to_image"
|
88 |
-
headers = {
|
89 |
-
"Authorization": f"Bearer {fireworks_api_key}",
|
90 |
-
"Content-Type": "application/json",
|
91 |
-
"Accept": "image/jpeg"
|
92 |
-
}
|
93 |
-
data = {
|
94 |
-
"prompt": prompt,
|
95 |
-
"aspect_ratio": "16:9",
|
96 |
-
"guidance_scale": 3.5,
|
97 |
-
"num_inference_steps": steps,
|
98 |
-
"seed": 0
|
99 |
-
}
|
100 |
-
|
101 |
-
# Send the request
|
102 |
-
response = requests.post(url, headers=headers, json=data)
|
103 |
-
|
104 |
-
if response.status_code == 200:
|
105 |
-
# Convert the response to an image
|
106 |
-
img_data = response.content
|
107 |
-
img = Image.open(BytesIO(img_data))
|
108 |
-
return img
|
109 |
-
else:
|
110 |
-
raise RuntimeError(f"Error with FLUX model {model_path}: {response.text}")
|
111 |
-
|
112 |
-
# Function to wrap text to a specified number of characters
|
113 |
-
def wrap_text(text, max_chars):
|
114 |
-
"""Wrap text to a specified number of characters per line."""
|
115 |
-
return "\n".join(textwrap.fill(line, width=max_chars) for line in text.split("\n"))
|
116 |
-
|
117 |
-
# Function to add the holiday message to the image
|
118 |
-
from PIL import ImageColor, ImageDraw, ImageFont, Image
|
119 |
-
|
120 |
-
# Function to add the holiday message to the image
|
121 |
-
def add_custom_message(image, message, font_path, font_size, position_vertical, position_horizontal, max_chars, bg_color, font_color, alpha):
|
122 |
-
try:
|
123 |
-
font = ImageFont.truetype(font_path, font_size)
|
124 |
-
except IOError:
|
125 |
-
font = ImageFont.load_default()
|
126 |
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
|
131 |
-
|
132 |
-
overlay = Image.new("RGBA", image.size, (255, 255, 255, 0)) # Fully transparent
|
133 |
|
134 |
-
|
|
|
|
|
|
|
|
|
135 |
|
136 |
-
|
137 |
-
message = wrap_text(message, max_chars)
|
138 |
|
139 |
-
|
|
|
140 |
|
141 |
-
|
142 |
-
|
143 |
-
line_height = draw.textbbox((0, 0), "A", font=font)[3] # Get height of one line of text
|
144 |
-
total_text_height = line_height * len(text_lines)
|
145 |
-
text_width = max([draw.textbbox((0, 0), line, font=font)[2] for line in text_lines])
|
146 |
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
x_pos = (img_width - text_width) // 2
|
152 |
-
else: # "Right"
|
153 |
-
x_pos = img_width - text_width - 10 # Padding from the right
|
154 |
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
y_pos = (img_height - total_text_height) // 2
|
160 |
-
else: # "Bottom"
|
161 |
-
y_pos = img_height - total_text_height - 10 # Padding from the bottom
|
162 |
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
# Convert the user-specified background color to RGBA and apply the alpha transparency value
|
167 |
-
bg_color_rgba = (*ImageColor.getrgb(bg_color), alpha) # Apply user-controlled transparency
|
168 |
-
|
169 |
-
# Draw the semi-transparent background rectangle on the overlay
|
170 |
-
draw.rectangle([x_pos - padding, y_pos - padding, x_pos + text_width + padding, y_pos + total_text_height + padding], fill=bg_color_rgba)
|
171 |
-
|
172 |
-
# Draw the text line by line with the user-specified font color
|
173 |
-
for i, line in enumerate(text_lines):
|
174 |
-
draw.text((x_pos, y_pos + i * line_height), line, font=font, fill=font_color)
|
175 |
-
|
176 |
-
# Composite the overlay with the original image
|
177 |
-
combined = Image.alpha_composite(image, overlay)
|
178 |
-
|
179 |
-
return combined.convert("RGB") # Convert back to RGB for saving/display
|
180 |
|
|
|
|
|
181 |
|
|
|
|
|
|
|
182 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
183 |
|
184 |
-
# Streamlit UI
|
185 |
-
st.title("FLUX Image Generation")
|
186 |
-
st.write("Generate images using the FLUX models and customize them with a holiday message.")
|
187 |
|
188 |
# Dropdown to select a holiday-themed prompt or enter a custom prompt
|
189 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
190 |
custom_prompt = st.text_input("Enter your custom prompt") if selected_prompt == "Custom" else ""
|
191 |
prompt = custom_prompt if selected_prompt == "Custom" else selected_prompt
|
192 |
|
193 |
# Dropdown to select the model
|
194 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
195 |
|
196 |
# Dropdown to select an example message or write a custom one
|
197 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
198 |
message = st.text_input("Enter a holiday message to add:", value=selected_message if selected_message != "Custom" else "")
|
199 |
|
200 |
# Additional inputs for customizing the message
|
|
|
|
|
|
|
|
|
|
|
|
|
201 |
font_choice = st.selectbox("Select a font:", list(fonts.keys()))
|
202 |
-
font_size = st.slider("Select font size:",
|
203 |
max_chars = st.slider("Max characters per line:", 10, 100, 40) # Slider to select character wrap limit
|
204 |
|
205 |
# Background color and font color pickers
|
206 |
bg_color = st.color_picker("Pick a background color for the text box:", "#FFFFFF")
|
207 |
font_color = st.color_picker("Pick a font color:", "#000000")
|
208 |
|
209 |
-
#
|
210 |
alpha = st.slider("Select transparency level for the background (0: fully transparent, 255: fully opaque)", 0, 255, 220)
|
211 |
|
212 |
# Position options for the message (vertical and horizontal)
|
213 |
position_vertical = st.radio("Select message vertical position:", ["Top", "Center", "Bottom"])
|
214 |
position_horizontal = st.radio("Select message horizontal position:", ["Left", "Center", "Right"])
|
215 |
|
|
|
|
|
|
|
|
|
|
|
216 |
|
217 |
# Button to generate images
|
218 |
-
if
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
219 |
if not prompt.strip():
|
220 |
st.error("Please provide a prompt.")
|
221 |
else:
|
222 |
try:
|
223 |
with st.spinner("Generating image..."):
|
224 |
# Determine steps based on model
|
225 |
-
steps = 30 if model_choice == "flux-1-dev" else 4
|
226 |
|
227 |
-
# Generate image
|
228 |
-
generated_image = generate_flux_image(model_choice, prompt, steps)
|
229 |
|
230 |
-
# Get the selected font path
|
231 |
font_path = fonts[font_choice]
|
232 |
|
233 |
-
# Add the holiday message to the generated image
|
234 |
image_with_message = add_custom_message(
|
235 |
generated_image.copy(), message, font_path, font_size,
|
236 |
position_vertical, position_horizontal, max_chars, bg_color, font_color, alpha
|
@@ -239,8 +156,18 @@ if st.button("Generate Image"):
|
|
239 |
# Display the image with the message
|
240 |
st.image(image_with_message, caption=f"Generated using {model_choice} with custom message", use_column_width=True)
|
241 |
|
242 |
-
# Preview the placement
|
243 |
st.write(f"Message preview (vertical position: {position_vertical}, horizontal position: {position_horizontal}, font: {font_choice}, size: {font_size}, max chars: {max_chars}, bg color: {bg_color}, font color: {font_color}, transparency: {alpha})")
|
244 |
|
245 |
except Exception as e:
|
246 |
st.error(f"An error occurred: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Generate_Holiday_Postcard.py
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
+
import streamlit as st
|
4 |
+
from utils.helper_utilities import generate_flux_image, add_custom_message
|
5 |
+
from utils.configuration import fonts, holiday_scene_prompts, example_holiday_messages
|
6 |
+
from dotenv import load_dotenv
|
7 |
+
import os
|
8 |
|
9 |
# Set page configuration
|
10 |
st.set_page_config(page_title="FLUX Image Generation Tool", page_icon="🎇")
|
11 |
|
12 |
+
# Streamlit UI Elements
|
13 |
+
st.title("🎇 FLUX-tastic Holiday Postcard Generator 🎨")
|
14 |
+
st.markdown(
|
15 |
+
"""Welcome to the FLUX Holiday Postcard Generator!
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
|
17 |
+
- 🎅🎨 It's time to make your holiday greetings pop with a personalized, AI-generated postcard!
|
18 |
+
- ✨ No more boring, store-bought cards! Instead, flex your creative muscles with the FLUX models, adding your unique touch.
|
19 |
+
- Customize a dazzling holiday scene with your own message and share your festive vibes! 🎁🎄
|
20 |
|
21 |
+
### How it works:
|
|
|
22 |
|
23 |
+
1. Choose a holiday-themed prompt or write your own 🎉
|
24 |
+
2. Select a FLUX model to bring your vision to life ✨
|
25 |
+
3. Customize with a snazzy holiday message and choose from a variety of fonts 🖋️
|
26 |
+
4. Design your text's background and color to perfectly match the vibe 🎨
|
27 |
+
5. Generate your masterpiece and share the joy with friends and family! 📬
|
28 |
|
29 |
+
Get ready to deck the halls with creativity! 🎄✨
|
|
|
30 |
|
31 |
+
"""
|
32 |
+
)
|
33 |
|
34 |
+
st.divider()
|
35 |
+
st.subheader("Load Fireworks API Key")
|
|
|
|
|
|
|
36 |
|
37 |
+
# Load API Key
|
38 |
+
# Define and ensure the .env directory and file exist
|
39 |
+
dotenv_path = os.path.join(os.path.dirname(__file__), '..', 'env', '.env')
|
40 |
+
os.makedirs(os.path.dirname(dotenv_path), exist_ok=True)
|
|
|
|
|
|
|
41 |
|
42 |
+
# Create the .env file if it doesn't exist
|
43 |
+
if not os.path.exists(dotenv_path):
|
44 |
+
with open(dotenv_path, "w") as f:
|
45 |
+
st.success(f"Created {dotenv_path}")
|
|
|
|
|
|
|
46 |
|
47 |
+
# Load environment variables from the .env file
|
48 |
+
load_dotenv(dotenv_path, override=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
|
50 |
+
# Check if the Fireworks API key is set or blank
|
51 |
+
fireworks_api_key = os.getenv("FIREWORKS_API_KEY")
|
52 |
|
53 |
+
# Show the entire app but disable running parts if no API key
|
54 |
+
if not fireworks_api_key or fireworks_api_key.strip() == "":
|
55 |
+
fireworks_api_key = st.text_input("Enter Fireworks API Key", type="password")
|
56 |
|
57 |
+
# Optionally, allow the user to save the API key to the .env file
|
58 |
+
if fireworks_api_key and st.checkbox("Save API key for future use"):
|
59 |
+
with open(dotenv_path, "a") as f:
|
60 |
+
f.write(f"FIREWORKS_API_KEY={fireworks_api_key}\n")
|
61 |
+
st.success("API key saved to .env file.")
|
62 |
+
else:
|
63 |
+
st.success(f"API key loaded successfully: partial preview {fireworks_api_key[:5]}")
|
64 |
|
|
|
|
|
|
|
65 |
|
66 |
# Dropdown to select a holiday-themed prompt or enter a custom prompt
|
67 |
+
st.divider()
|
68 |
+
st.subheader("1️⃣ Step 1: Pick Your Holiday Theme or Create Your Own 🎄✨")
|
69 |
+
st.markdown("""
|
70 |
+
Get into the festive spirit by choosing from a range of holiday-inspired prompts. Feeling extra creative? Enter your own prompt and let the holiday magic begin! 🎅✨
|
71 |
+
""")
|
72 |
+
|
73 |
+
selected_prompt = st.selectbox("Choose a holiday-themed prompt or enter your own", options=["Custom"] + holiday_scene_prompts)
|
74 |
custom_prompt = st.text_input("Enter your custom prompt") if selected_prompt == "Custom" else ""
|
75 |
prompt = custom_prompt if selected_prompt == "Custom" else selected_prompt
|
76 |
|
77 |
# Dropdown to select the model
|
78 |
+
st.divider()
|
79 |
+
st.subheader("2️⃣ Step 2: Select Your FLUX Model 🚀")
|
80 |
+
st.markdown("""
|
81 |
+
Choose from two FLUX models: whether you’re aiming for lightning speed or extra detail, we’ve got you covered! 💥✨
|
82 |
+
""")
|
83 |
+
|
84 |
+
model_choice = st.selectbox("Select the model:", ["flux-1-schnell-fp8", "flux-1-dev-fp8"])
|
85 |
|
86 |
# Dropdown to select an example message or write a custom one
|
87 |
+
st.divider()
|
88 |
+
st.subheader("3️⃣ Step 3: Craft the Perfect Message 🎁")
|
89 |
+
st.markdown("""
|
90 |
+
What’s a holiday card without a heartfelt (or hilarious) message? Choose from example holiday greetings or write your own to make the card so you! 🖋️🎉
|
91 |
+
""")
|
92 |
+
|
93 |
+
selected_message = st.selectbox("Choose an example message or write your own:", options=["Custom"] + example_holiday_messages)
|
94 |
message = st.text_input("Enter a holiday message to add:", value=selected_message if selected_message != "Custom" else "")
|
95 |
|
96 |
# Additional inputs for customizing the message
|
97 |
+
st.divider()
|
98 |
+
st.subheader("4️⃣ Step 4: Style Your Message with Flair ✨")
|
99 |
+
st.markdown("""
|
100 |
+
From fancy fonts to colorful backgrounds, you’re in control. Pick your favorite font, adjust the size, and add a splash of color to make your message truly shine. 🌈🎨
|
101 |
+
""")
|
102 |
+
|
103 |
font_choice = st.selectbox("Select a font:", list(fonts.keys()))
|
104 |
+
font_size = st.slider("Select font size:", 1, 300, 40)
|
105 |
max_chars = st.slider("Max characters per line:", 10, 100, 40) # Slider to select character wrap limit
|
106 |
|
107 |
# Background color and font color pickers
|
108 |
bg_color = st.color_picker("Pick a background color for the text box:", "#FFFFFF")
|
109 |
font_color = st.color_picker("Pick a font color:", "#000000")
|
110 |
|
111 |
+
# Transparency level slider
|
112 |
alpha = st.slider("Select transparency level for the background (0: fully transparent, 255: fully opaque)", 0, 255, 220)
|
113 |
|
114 |
# Position options for the message (vertical and horizontal)
|
115 |
position_vertical = st.radio("Select message vertical position:", ["Top", "Center", "Bottom"])
|
116 |
position_horizontal = st.radio("Select message horizontal position:", ["Left", "Center", "Right"])
|
117 |
|
118 |
+
st.divider()
|
119 |
+
st.subheader("5️⃣ Step 5: Preview and Share the Holiday Cheer! 🎅📬")
|
120 |
+
st.markdown("""
|
121 |
+
Click "Generate Image" and watch the magic happen! Your holiday card is just moments away from spreading joy to everyone on your list. 🎄🎁✨
|
122 |
+
""")
|
123 |
|
124 |
# Button to generate images
|
125 |
+
if not fireworks_api_key or fireworks_api_key.strip() == "":
|
126 |
+
st.warning("Enter a valid Fireworks API key to enable image generation.")
|
127 |
+
generate_button = st.button("Generate Image", disabled=True)
|
128 |
+
else:
|
129 |
+
generate_button = st.button("Generate Image")
|
130 |
+
|
131 |
+
|
132 |
+
if generate_button:
|
133 |
+
st.markdown("""
|
134 |
+
🎉 You're one click away from holiday magic! 🎉 Hit that Generate Image button and let FLUX create your personalized postcard—ready for sharing! 📬
|
135 |
+
""")
|
136 |
if not prompt.strip():
|
137 |
st.error("Please provide a prompt.")
|
138 |
else:
|
139 |
try:
|
140 |
with st.spinner("Generating image..."):
|
141 |
# Determine steps based on model
|
142 |
+
steps = 30 if model_choice == "flux-1-dev-fp8" else 4
|
143 |
|
144 |
+
# Generate image using the helper utility
|
145 |
+
generated_image = generate_flux_image(model_choice, prompt, steps, fireworks_api_key)
|
146 |
|
147 |
+
# Get the selected font path from configuration
|
148 |
font_path = fonts[font_choice]
|
149 |
|
150 |
+
# Add the holiday message to the generated image using the helper utility
|
151 |
image_with_message = add_custom_message(
|
152 |
generated_image.copy(), message, font_path, font_size,
|
153 |
position_vertical, position_horizontal, max_chars, bg_color, font_color, alpha
|
|
|
156 |
# Display the image with the message
|
157 |
st.image(image_with_message, caption=f"Generated using {model_choice} with custom message", use_column_width=True)
|
158 |
|
159 |
+
# Preview the placement details
|
160 |
st.write(f"Message preview (vertical position: {position_vertical}, horizontal position: {position_horizontal}, font: {font_choice}, size: {font_size}, max chars: {max_chars}, bg color: {bg_color}, font color: {font_color}, transparency: {alpha})")
|
161 |
|
162 |
except Exception as e:
|
163 |
st.error(f"An error occurred: {e}")
|
164 |
+
|
165 |
+
# Footer Section
|
166 |
+
st.divider()
|
167 |
+
st.markdown(
|
168 |
+
"""
|
169 |
+
Thank you for using the Holiday Card Generator powered by **Fireworks**! 🎉
|
170 |
+
Share your creations with the world and spread the holiday cheer!
|
171 |
+
Happy Holidays from the **Fireworks Team**. 💥
|
172 |
+
"""
|
173 |
+
)
|
pages/2_Generate_Holiday_Borders.py
DELETED
@@ -1,371 +0,0 @@
|
|
1 |
-
import streamlit as st
|
2 |
-
import cv2
|
3 |
-
import requests
|
4 |
-
from io import BytesIO
|
5 |
-
from PIL import Image, ImageDraw
|
6 |
-
import numpy as np
|
7 |
-
import os
|
8 |
-
from dotenv import load_dotenv
|
9 |
-
import zipfile
|
10 |
-
|
11 |
-
|
12 |
-
# Load environment variables
|
13 |
-
dotenv_path = os.path.join(os.path.dirname(__file__), '../env/.env')
|
14 |
-
load_dotenv(dotenv_path, override=True)
|
15 |
-
api_key = os.getenv("FIREWORKS_API_KEY")
|
16 |
-
|
17 |
-
if not api_key:
|
18 |
-
st.error("API key not found. Make sure FIREWORKS_API_KEY is set in the .env file.")
|
19 |
-
st.stop()
|
20 |
-
|
21 |
-
VALID_ASPECT_RATIOS = {
|
22 |
-
(1, 1): "1:1", (21, 9): "21:9", (16, 9): "16:9", (3, 2): "3:2", (5, 4): "5:4",
|
23 |
-
(4, 5): "4:5", (2, 3): "2:3", (9, 16): "9:16", (9, 21): "9:21",
|
24 |
-
}
|
25 |
-
|
26 |
-
def get_closest_aspect_ratio(width, height):
|
27 |
-
aspect_ratio = width / height
|
28 |
-
closest_ratio = min(VALID_ASPECT_RATIOS.keys(), key=lambda x: abs((x[0] / x[1]) - aspect_ratio))
|
29 |
-
return VALID_ASPECT_RATIOS[closest_ratio]
|
30 |
-
|
31 |
-
def process_image(uploaded_image):
|
32 |
-
image = np.array(Image.open(uploaded_image).convert('L'))
|
33 |
-
edges = cv2.Canny(image, 100, 200)
|
34 |
-
edges_rgb = cv2.cvtColor(edges, cv2.COLOR_GRAY2RGB)
|
35 |
-
pil_image = Image.fromarray(edges_rgb)
|
36 |
-
byte_arr = BytesIO()
|
37 |
-
pil_image.save(byte_arr, format='JPEG')
|
38 |
-
byte_arr.seek(0)
|
39 |
-
return byte_arr, pil_image
|
40 |
-
|
41 |
-
def call_control_net_api(uploaded_image, prompt, control_mode=0, guidance_scale=3.5, num_inference_steps=30, seed=0, controlnet_conditioning_scale=1.0):
|
42 |
-
control_image, processed_image = process_image(uploaded_image)
|
43 |
-
files = {'control_image': ('control_image.jpg', control_image, 'image/jpeg')}
|
44 |
-
original_image = Image.open(uploaded_image)
|
45 |
-
width, height = original_image.size
|
46 |
-
aspect_ratio = get_closest_aspect_ratio(width, height)
|
47 |
-
data = {
|
48 |
-
'prompt': prompt,
|
49 |
-
'control_mode': control_mode,
|
50 |
-
'aspect_ratio': aspect_ratio,
|
51 |
-
'guidance_scale': guidance_scale,
|
52 |
-
'num_inference_steps': num_inference_steps,
|
53 |
-
'seed': seed,
|
54 |
-
'controlnet_conditioning_scale': controlnet_conditioning_scale
|
55 |
-
}
|
56 |
-
headers = {
|
57 |
-
'accept': 'image/jpeg',
|
58 |
-
'authorization': f'Bearer {api_key}',
|
59 |
-
}
|
60 |
-
response = requests.post('https://api.fireworks.ai/inference/v1/workflows/accounts/fireworks/models/flux-1-dev-controlnet-union/control_net',
|
61 |
-
files=files, data=data, headers=headers)
|
62 |
-
if response.status_code == 200:
|
63 |
-
return Image.open(BytesIO(response.content)), processed_image, original_image
|
64 |
-
else:
|
65 |
-
st.error(f"Request failed with status code: {response.status_code}, Response: {response.text}")
|
66 |
-
return None, None, None
|
67 |
-
|
68 |
-
def draw_crop_preview(image, x, y, width, height):
|
69 |
-
draw = ImageDraw.Draw(image)
|
70 |
-
draw.rectangle([x, y, x + width, y + height], outline="red", width=2)
|
71 |
-
return image
|
72 |
-
|
73 |
-
|
74 |
-
# Fireworks Logo at the top
|
75 |
-
logo_image = Image.open("img/fireworksai_logo.png")
|
76 |
-
st.image(logo_image)
|
77 |
-
|
78 |
-
st.title("🎨 Holiday Card Generator - Part A: Design & Ideation 🎨")
|
79 |
-
st.markdown(
|
80 |
-
"""Welcome to the first part of your holiday card creation journey! 🌟 Here, you’ll play around with different styles, prompts, and parameters to design the perfect card border before adding a personal message in Part B. Let your creativity flow! 🎉
|
81 |
-
|
82 |
-
### How it works:
|
83 |
-
|
84 |
-
1. **🖼️ Upload Your Image:** Choose the image that will be the center of your card.
|
85 |
-
2. **✂️ Crop It:** Adjust the crop to highlight the most important part of your image.
|
86 |
-
3. **💡 Choose Your Style:** Select from festive border themes or input your own custom prompt to design something unique.
|
87 |
-
4. **⚙️ Fine-Tune Parameters:** Experiment with guidance scales, seeds, inference steps, and more for the perfect aesthetic.
|
88 |
-
5. **👀 Preview & Download:** See your generated holiday cards, tweak them until they’re just right, and download the final designs and metadata in a neat ZIP file!
|
89 |
-
|
90 |
-
Once you’ve got the perfect look, head over to **Part B** to add your personal message and finalize your holiday card! 💌
|
91 |
-
|
92 |
-
"""
|
93 |
-
)
|
94 |
-
|
95 |
-
# Add divider and subheader for uploading section
|
96 |
-
st.divider()
|
97 |
-
st.subheader("🖼️ Step 1: Upload Your Picture!")
|
98 |
-
st.markdown("""
|
99 |
-
- Click on the **Upload Image** button to select the image you want to feature on your holiday card.
|
100 |
-
- Accepted formats: **JPG** or **PNG**.
|
101 |
-
- Once uploaded, your image will appear on the screen—easy peasy!
|
102 |
-
""")
|
103 |
-
st.write("Upload the image that will be used for generating your holiday card.")
|
104 |
-
|
105 |
-
# Initialize session state variables if they don't exist
|
106 |
-
if 'uploaded_file' not in st.session_state:
|
107 |
-
st.session_state.uploaded_file = None
|
108 |
-
if 'card_params' not in st.session_state:
|
109 |
-
st.session_state.card_params = [{} for _ in range(4)] # Initialize for 4 cards
|
110 |
-
if 'generated_cards' not in st.session_state:
|
111 |
-
st.session_state.generated_cards = [None for _ in range(4)] # Store generated images and metadata
|
112 |
-
|
113 |
-
# File uploader - if a file is uploaded, save it in session_state
|
114 |
-
uploaded_file = st.file_uploader("Upload an image", type=["png", "jpg", "jpeg"])
|
115 |
-
st.divider()
|
116 |
-
|
117 |
-
if uploaded_file is not None:
|
118 |
-
st.session_state.uploaded_file = uploaded_file # Save to session state
|
119 |
-
|
120 |
-
# Load the image from session state
|
121 |
-
if st.session_state.uploaded_file is not None:
|
122 |
-
original_image = Image.open(st.session_state.uploaded_file)
|
123 |
-
st.image(original_image, caption="Uploaded Image", use_column_width=True)
|
124 |
-
|
125 |
-
img_width, img_height = original_image.size
|
126 |
-
|
127 |
-
# Add a divider and subheader for the crop section
|
128 |
-
st.divider()
|
129 |
-
st.subheader("✂️ Step 2: Crop It Like It's Hot!")
|
130 |
-
st.markdown("""
|
131 |
-
- Adjust the **crop** sliders to select the perfect area of your image.
|
132 |
-
- This cropped section will be the centerpiece of your festive card. 🎨
|
133 |
-
- A preview will show you exactly how your crop looks before moving to the next step!
|
134 |
-
""")
|
135 |
-
st.write("Select the area you want to crop from the original image. This cropped portion will be used in the final card.")
|
136 |
-
|
137 |
-
col1, col2 = st.columns(2)
|
138 |
-
with col1:
|
139 |
-
x_pos = st.slider("X position (Left-Right)", 0, img_width, img_width // 4,
|
140 |
-
help="Move the slider to adjust the crop's left-right position.")
|
141 |
-
crop_width = st.slider("Width", 10, img_width - x_pos, min(img_width // 2, img_width - x_pos),
|
142 |
-
help="Adjust the width of the crop.")
|
143 |
-
with col2:
|
144 |
-
y_pos = st.slider("Y position (Up-Down)", 0, img_height, img_height // 4,
|
145 |
-
help="Move the slider to adjust the crop's up-down position.")
|
146 |
-
crop_height = st.slider("Height", 10, img_height - y_pos, min(img_height // 2, img_height - y_pos),
|
147 |
-
help="Adjust the height of the crop.")
|
148 |
-
|
149 |
-
preview_image = draw_crop_preview(original_image.copy(), x_pos, y_pos, crop_width, crop_height)
|
150 |
-
st.image(preview_image, caption="Crop Preview", use_column_width=True)
|
151 |
-
st.divider()
|
152 |
-
|
153 |
-
st.subheader("⚙️ Step 3: Set Your Festive Border Design with Flux + Fireworks!")
|
154 |
-
st.markdown("""
|
155 |
-
- Choose from a selection of holiday-themed borders like **snowflakes**, **Christmas lights**, or even **New Year's Eve fireworks**—all generated through **Flux models on Fireworks!** ✨
|
156 |
-
- Want to get creative? No problem—enter your own **custom prompt** to create a border that's as unique as you are, all powered by **Fireworks' inference API**.
|
157 |
-
""")
|
158 |
-
st.write("Customize the parameters for generating each holiday card. Each parameter will influence the final design and style of the card.")
|
159 |
-
|
160 |
-
# Define the list of suggested holiday prompts
|
161 |
-
holiday_prompts = [
|
162 |
-
"A border of Festive snowflakes and winter patterns for a holiday card",
|
163 |
-
"A border of Joyful Christmas ornaments and lights decorating the edges",
|
164 |
-
"A border of Warm and cozy fireplace scene with stockings and garlands",
|
165 |
-
"A border of Colorful Hanukkah menorahs and dreidels along the border",
|
166 |
-
"A border of New Year's Eve fireworks with stars and confetti framing the image",
|
167 |
-
"A border of Samhain symbols with Celtic knots, pumpkins, and candles for a mystical October celebration",
|
168 |
-
"A border of Día de Muertos skulls (calaveras), marigold flowers, and candles in vibrant colors",
|
169 |
-
"A border of Yule imagery with holly, pine trees, and Norse winter symbols under the starry night sky",
|
170 |
-
"A border of Diwali lamps (diyas), rangoli patterns, and lotus flowers for a bright and colorful festival",
|
171 |
-
"A border of Dongzhi Festival glutinous rice balls and cozy winter imagery, celebrating family gatherings",
|
172 |
-
"A border of Soyal dancers, sun symbols, and traditional Zuni and Hopi winter motifs to welcome the sun's return",
|
173 |
-
"A border of Winter Solstice symbols with bare trees, stars, and the moon against a cold winter night",
|
174 |
-
"A border of spooky Halloween elements like pumpkins, bats, ghosts, and cobwebs for a haunted theme",
|
175 |
-
"A border of fall harvest items like pumpkins, cornucopias, autumn leaves, and turkeys",
|
176 |
-
"A border of Christmas ornaments, wreaths, and lights for a joyful, festive holiday look"
|
177 |
-
]
|
178 |
-
|
179 |
-
# Define input fields for each holiday card's parameters in expanders
|
180 |
-
for i in range(4):
|
181 |
-
with st.expander(f"Holiday Card {i + 1} Parameters"):
|
182 |
-
st.write(f"### Holiday Card {i + 1}")
|
183 |
-
|
184 |
-
# Get the card params from session_state if they exist
|
185 |
-
card_params = st.session_state.card_params[i]
|
186 |
-
card_params.setdefault("prompt", f"Custom Prompt {i + 1}")
|
187 |
-
card_params.setdefault("guidance_scale", 3.5)
|
188 |
-
card_params.setdefault("num_inference_steps", 30)
|
189 |
-
card_params.setdefault("seed", i * 100)
|
190 |
-
card_params.setdefault("controlnet_conditioning_scale", 0.5)
|
191 |
-
card_params.setdefault("control_mode", 0)
|
192 |
-
|
193 |
-
# Dropdown to choose a suggested holiday prompt or enter custom prompt
|
194 |
-
selected_prompt = st.selectbox(f"Choose a holiday-themed prompt for Holiday Card {i + 1}", options=["Custom"] + holiday_prompts)
|
195 |
-
custom_prompt = st.text_input(f"Enter custom prompt for Holiday Card {i + 1}", value=card_params["prompt"]) if selected_prompt == "Custom" else selected_prompt
|
196 |
-
|
197 |
-
# Parameter sliders for each holiday card with tooltips
|
198 |
-
guidance_scale = st.slider(f"Guidance Scale for Holiday Card {i + 1}", min_value=0.0, max_value=20.0, value=card_params["guidance_scale"], step=0.1,
|
199 |
-
help="Adjusts how strongly the model follows the prompt. Higher values mean stronger adherence to the prompt.")
|
200 |
-
num_inference_steps = st.slider(f"Number of Inference Steps for Holiday Card {i + 1}", min_value=1, max_value=100, value=card_params["num_inference_steps"], step=1,
|
201 |
-
help="More inference steps can lead to a higher quality image but will take longer to generate.")
|
202 |
-
seed = st.slider(f"Random Seed for Holiday Card {i + 1}", min_value=0, max_value=1000, value=card_params["seed"],
|
203 |
-
help="The seed value allows you to recreate the same image each time, or explore variations by changing the seed.")
|
204 |
-
controlnet_conditioning_scale = st.slider(f"ControlNet Conditioning Scale for Holiday Card {i + 1}", min_value=0.0, max_value=1.0, value=card_params["controlnet_conditioning_scale"], step=0.1,
|
205 |
-
help="Controls how much the ControlNet input influences the output. A lower value reduces its influence.")
|
206 |
-
control_mode = st.slider(f"Control Mode for Holiday Card {i + 1}", min_value=0, max_value=2, value=card_params["control_mode"],
|
207 |
-
help="Choose how much ControlNet should influence the final image. 0: None, 1: Partial, 2: Full.")
|
208 |
-
|
209 |
-
# Update session state with the latest parameters
|
210 |
-
st.session_state.card_params[i] = {
|
211 |
-
"prompt": custom_prompt,
|
212 |
-
"guidance_scale": guidance_scale,
|
213 |
-
"num_inference_steps": num_inference_steps,
|
214 |
-
"seed": seed,
|
215 |
-
"controlnet_conditioning_scale": controlnet_conditioning_scale,
|
216 |
-
"control_mode": control_mode
|
217 |
-
}
|
218 |
-
|
219 |
-
st.divider() # Add a divider before the "Generate" button
|
220 |
-
|
221 |
-
# Generate the holiday cards
|
222 |
-
st.subheader("Just hit play!")
|
223 |
-
if st.button("Generate Holiday Cards"):
|
224 |
-
with st.spinner("Processing..."):
|
225 |
-
# Create a column layout for displaying cards side by side
|
226 |
-
col1, col2 = st.columns(2)
|
227 |
-
col3, col4 = st.columns(2)
|
228 |
-
columns = [col1, col2, col3, col4] # To display images in a 2x2 grid
|
229 |
-
|
230 |
-
# Store image bytes and metadata in lists
|
231 |
-
image_files = []
|
232 |
-
metadata = []
|
233 |
-
|
234 |
-
# Loop through each card's parameters and generate the holiday card
|
235 |
-
for i, params in enumerate(st.session_state.card_params):
|
236 |
-
prompt = params['prompt']
|
237 |
-
guidance_scale = params['guidance_scale']
|
238 |
-
num_inference_steps = params['num_inference_steps']
|
239 |
-
seed = params['seed']
|
240 |
-
controlnet_conditioning_scale = params['controlnet_conditioning_scale']
|
241 |
-
control_mode = params['control_mode']
|
242 |
-
|
243 |
-
# Generate the holiday card using the current parameters
|
244 |
-
generated_image, processed_image, _ = call_control_net_api(
|
245 |
-
st.session_state.uploaded_file, prompt, control_mode=control_mode,
|
246 |
-
guidance_scale=guidance_scale, num_inference_steps=num_inference_steps,
|
247 |
-
seed=seed, controlnet_conditioning_scale=controlnet_conditioning_scale
|
248 |
-
)
|
249 |
-
|
250 |
-
if generated_image is not None:
|
251 |
-
# Resize generated_image to match original_image size
|
252 |
-
generated_image = generated_image.resize(original_image.size)
|
253 |
-
|
254 |
-
# Create a copy of the generated image
|
255 |
-
final_image = generated_image.copy()
|
256 |
-
|
257 |
-
# Crop the selected portion of the original image
|
258 |
-
cropped_original = original_image.crop((x_pos, y_pos, x_pos + crop_width, y_pos + crop_height))
|
259 |
-
|
260 |
-
# Get the size of the cropped image
|
261 |
-
cropped_width, cropped_height = cropped_original.size
|
262 |
-
|
263 |
-
# Calculate the center of the generated image
|
264 |
-
center_x = (final_image.width - cropped_width) // 2
|
265 |
-
center_y = (final_image.height - cropped_height) // 2
|
266 |
-
|
267 |
-
# Paste the cropped portion of the original image onto the generated image at the calculated center
|
268 |
-
final_image.paste(cropped_original, (center_x, center_y))
|
269 |
-
|
270 |
-
# Save image to BytesIO
|
271 |
-
img_byte_arr = BytesIO()
|
272 |
-
final_image.save(img_byte_arr, format="PNG")
|
273 |
-
img_byte_arr.seek(0)
|
274 |
-
image_files.append((f"holiday_card_{i + 1}.png", img_byte_arr))
|
275 |
-
|
276 |
-
# Store metadata
|
277 |
-
metadata.append({
|
278 |
-
"Card": f"Holiday Card {i + 1}",
|
279 |
-
"Prompt": prompt,
|
280 |
-
"Guidance Scale": guidance_scale,
|
281 |
-
"Inference Steps": num_inference_steps,
|
282 |
-
"Seed": seed,
|
283 |
-
"ControlNet Conditioning Scale": controlnet_conditioning_scale,
|
284 |
-
"Control Mode": control_mode
|
285 |
-
})
|
286 |
-
|
287 |
-
# Persist image and metadata in session state
|
288 |
-
st.session_state.generated_cards[i] = {
|
289 |
-
"image": final_image,
|
290 |
-
"metadata": metadata[-1]
|
291 |
-
}
|
292 |
-
|
293 |
-
# Display the final holiday card in one of the columns
|
294 |
-
columns[i].image(final_image, caption=f"Holiday Card {i + 1}", use_column_width=True)
|
295 |
-
|
296 |
-
# Display the parameters used for this card
|
297 |
-
columns[i].write(f"**Prompt:** {prompt}")
|
298 |
-
columns[i].write(f"**Guidance Scale:** {guidance_scale}")
|
299 |
-
columns[i].write(f"**Inference Steps:** {num_inference_steps}")
|
300 |
-
columns[i].write(f"**Seed:** {seed}")
|
301 |
-
columns[i].write(f"**ControlNet Conditioning Scale:** {controlnet_conditioning_scale}")
|
302 |
-
columns[i].write(f"**Control Mode:** {control_mode}")
|
303 |
-
else:
|
304 |
-
st.error(f"Failed to generate holiday card {i + 1}. Please try again.")
|
305 |
-
|
306 |
-
# Create ZIP file for download
|
307 |
-
if image_files:
|
308 |
-
zip_buffer = BytesIO()
|
309 |
-
with zipfile.ZipFile(zip_buffer, "w") as zf:
|
310 |
-
# Add images to the ZIP file
|
311 |
-
for file_name, img_data in image_files:
|
312 |
-
zf.writestr(file_name, img_data.getvalue())
|
313 |
-
|
314 |
-
# Add metadata to the ZIP file
|
315 |
-
metadata_str = "\n\n".join([f"{m['Card']}:\nPrompt: {m['Prompt']}\nGuidance Scale: {m['Guidance Scale']}\nInference Steps: {m['Inference Steps']}\nSeed: {m['Seed']}\nControlNet Conditioning Scale: {m['ControlNet Conditioning Scale']}\nControl Mode: {m['Control Mode']}" for m in metadata])
|
316 |
-
zf.writestr("metadata.txt", metadata_str)
|
317 |
-
|
318 |
-
zip_buffer.seek(0)
|
319 |
-
st.subheader("Step 4: Download & Share Your Masterpiece! 📥")
|
320 |
-
st.markdown("""
|
321 |
-
- Once you're happy with your card, simply hit the download button to save your card and message as a **PNG** image.
|
322 |
-
- You can also view and download any **previously generated holiday cards** from this session!
|
323 |
-
""")
|
324 |
-
st.download_button(
|
325 |
-
label="Download all images and metadata as ZIP",
|
326 |
-
data=zip_buffer,
|
327 |
-
file_name="holiday_cards.zip",
|
328 |
-
mime="application/zip"
|
329 |
-
)
|
330 |
-
|
331 |
-
# Display previously generated cards if they exist and create a ZIP download button
|
332 |
-
st.divider()
|
333 |
-
st.subheader("Previously Generated Holiday Cards")
|
334 |
-
if any(st.session_state.generated_cards):
|
335 |
-
col1, col2 = st.columns(2)
|
336 |
-
col3, col4 = st.columns(2)
|
337 |
-
columns = [col1, col2, col3, col4] # Display in 2x2 grid
|
338 |
-
|
339 |
-
image_files = []
|
340 |
-
metadata = [] # Ensure metadata is initialized as a list
|
341 |
-
|
342 |
-
# Loop through previously generated cards
|
343 |
-
for i, card in enumerate(st.session_state.generated_cards):
|
344 |
-
if card and "metadata" in card: # Ensure the card exists and has metadata
|
345 |
-
columns[i].image(card['image'], caption=f"Holiday Card {i + 1} (Saved)")
|
346 |
-
card_metadata = card['metadata'] # Retrieve the metadata for the current card
|
347 |
-
columns[i].write(f"**Prompt:** {card_metadata['Prompt']}")
|
348 |
-
columns[i].write(f"**Guidance Scale:** {card_metadata['Guidance Scale']}")
|
349 |
-
columns[i].write(f"**Inference Steps:** {card_metadata['Inference Steps']}")
|
350 |
-
columns[i].write(f"**Seed:** {card_metadata['Seed']}")
|
351 |
-
columns[i].write(f"**ControlNet Conditioning Scale:** {card_metadata['ControlNet Conditioning Scale']}")
|
352 |
-
columns[i].write(f"**Control Mode:** {card_metadata['Control Mode']}")
|
353 |
-
|
354 |
-
# Add each image and its metadata to prepare for ZIP download
|
355 |
-
img_byte_arr = BytesIO()
|
356 |
-
card['image'].save(img_byte_arr, format="PNG")
|
357 |
-
img_byte_arr.seek(0)
|
358 |
-
image_files.append((f"holiday_card_{i + 1}.png", img_byte_arr))
|
359 |
-
metadata.append(card_metadata) # Append card's metadata to the metadata list
|
360 |
-
|
361 |
-
# Provide a ZIP download button for previously generated cards
|
362 |
-
if image_files:
|
363 |
-
zip_buffer = BytesIO()
|
364 |
-
with zipfile.ZipFile(zip_buffer, "w") as zf:
|
365 |
-
# Add images to the ZIP file
|
366 |
-
for file_name, img_data in image_files:
|
367 |
-
zf.writestr(file_name, img_data.getvalue())
|
368 |
-
|
369 |
-
# Add metadata to the ZIP file
|
370 |
-
metadata_str = "\n\n".join([f"{m['Card']}:\nPrompt: {m['Prompt']}\nGuidance Scale: {m['Guidance Scale']}\nInference Steps: {m['Inference Steps']}\nSeed: {m['Seed']}\nControlNet Conditioning Scale: {m['ControlNet Conditioning Scale']}\nControl Mode: {m['Control Mode']}" for m in metadata])
|
371 |
-
zf.writestr("metadata.txt", metadata_str)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pages/2_Generate_Multiple_Holiday_Borders.py
ADDED
@@ -0,0 +1,249 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Generate_holiday_borders.py
|
2 |
+
|
3 |
+
import streamlit as st
|
4 |
+
import os
|
5 |
+
from PIL import Image
|
6 |
+
from io import BytesIO
|
7 |
+
import zipfile
|
8 |
+
from dotenv import load_dotenv
|
9 |
+
|
10 |
+
# Import from helper_utilities.py
|
11 |
+
from utils.helper_utilities import (
|
12 |
+
get_closest_aspect_ratio, process_image, generate_flux_image, # Replace ControlNet API call with Flux API call
|
13 |
+
draw_crop_preview, combine_images, get_next_largest_aspect_ratio
|
14 |
+
)
|
15 |
+
|
16 |
+
# Import from configuration.py
|
17 |
+
from utils.configuration import (
|
18 |
+
default_guidance_scale,
|
19 |
+
default_num_inference_steps, default_seed,
|
20 |
+
holiday_border_prompts
|
21 |
+
)
|
22 |
+
|
23 |
+
|
24 |
+
|
25 |
+
# Initialize session state
|
26 |
+
if 'uploaded_file' not in st.session_state:
|
27 |
+
st.session_state.uploaded_file = None
|
28 |
+
if 'card_params' not in st.session_state:
|
29 |
+
st.session_state.card_params = [{} for _ in range(4)]
|
30 |
+
if 'generated_cards' not in st.session_state:
|
31 |
+
st.session_state.generated_cards = [None for _ in range(4)]
|
32 |
+
|
33 |
+
# Streamlit app starts here
|
34 |
+
st.image("img/fireworksai_logo.png")
|
35 |
+
st.title("🎨 Holiday Multi-Card Generator🎨")
|
36 |
+
st.markdown(
|
37 |
+
"""Welcome to the first part of your holiday card creation journey! 🌟 Here, you'll play around with different styles, prompts, and parameters to design the perfect card border before adding a personal message in the section 'Customize Holiday Borders'. Let your creativity flow! 🎉
|
38 |
+
|
39 |
+
### How it works:
|
40 |
+
|
41 |
+
1. **🖼️ Upload Your Image:** Choose the image that will be the center of your card.
|
42 |
+
2. **✂️ Crop It:** Adjust the crop to highlight the most important part of your image.
|
43 |
+
3. **💡 Choose Your Style:** Select from festive border themes or input your own custom prompt to design something unique.
|
44 |
+
4. **⚙️ Fine-Tune Parameters:** Experiment with guidance scales, seeds, inference steps, and more for the perfect aesthetic.
|
45 |
+
5. **👀 Preview & Download:** See your generated holiday cards, tweak them until they're just right, and download the final designs and metadata in a neat ZIP file!
|
46 |
+
|
47 |
+
Once you've got the perfect look, head over to **Part B** to add your personal message and finalize your holiday card! 💌
|
48 |
+
"""
|
49 |
+
)
|
50 |
+
|
51 |
+
# Load API Key
|
52 |
+
st.divider()
|
53 |
+
st.subheader("Load Fireworks API Key")
|
54 |
+
|
55 |
+
# Define and ensure the .env directory and file exist
|
56 |
+
dotenv_path = os.path.join(os.path.dirname(__file__), '..', 'env', '.env')
|
57 |
+
os.makedirs(os.path.dirname(dotenv_path), exist_ok=True)
|
58 |
+
|
59 |
+
# Create the .env file if it doesn't exist
|
60 |
+
if not os.path.exists(dotenv_path):
|
61 |
+
with open(dotenv_path, "w") as f:
|
62 |
+
st.success(f"Created {dotenv_path}")
|
63 |
+
|
64 |
+
# Load environment variables from the .env file
|
65 |
+
load_dotenv(dotenv_path, override=True)
|
66 |
+
|
67 |
+
# Check if the Fireworks API key is set or blank
|
68 |
+
fireworks_api_key = os.getenv("FIREWORKS_API_KEY")
|
69 |
+
|
70 |
+
# Show the entire app but disable running parts if no API key
|
71 |
+
if not fireworks_api_key or fireworks_api_key.strip() == "":
|
72 |
+
fireworks_api_key = st.text_input("Enter Fireworks API Key", type="password")
|
73 |
+
|
74 |
+
# Optionally, allow the user to save the API key to the .env file
|
75 |
+
if fireworks_api_key and st.checkbox("Save API key for future use"):
|
76 |
+
with open(dotenv_path, "a") as f:
|
77 |
+
f.write(f"FIREWORKS_API_KEY={fireworks_api_key}\n")
|
78 |
+
st.success("API key saved to .env file.")
|
79 |
+
else:
|
80 |
+
st.success(f"API key loaded successfully: partial preview {fireworks_api_key[:5]}")
|
81 |
+
|
82 |
+
# Step 1: Upload Image
|
83 |
+
st.divider()
|
84 |
+
st.subheader("🖼️ Step 1: Upload Your Picture!")
|
85 |
+
uploaded_file = st.file_uploader("Upload an image", type=["png", "jpg", "jpeg"])
|
86 |
+
if uploaded_file is not None:
|
87 |
+
st.session_state.uploaded_file = uploaded_file
|
88 |
+
original_image = Image.open(uploaded_file)
|
89 |
+
img_width, img_height = original_image.size
|
90 |
+
|
91 |
+
# Calculate the next largest valid aspect ratio
|
92 |
+
aspect_ratio = get_next_largest_aspect_ratio(img_width, img_height) # Ensure the aspect ratio is valid
|
93 |
+
|
94 |
+
st.image(original_image, caption="Uploaded Image", use_column_width=True)
|
95 |
+
|
96 |
+
# Step 2: Crop Image
|
97 |
+
st.divider()
|
98 |
+
st.subheader("✂️ Step 2: Crop It Like It's Hot!")
|
99 |
+
img_width, img_height = original_image.size
|
100 |
+
col1, col2 = st.columns(2)
|
101 |
+
with col1:
|
102 |
+
x_pos = st.slider("X position (Left-Right)", 0, img_width, img_width // 4)
|
103 |
+
crop_width = st.slider("Width", 10, img_width - x_pos, min(img_width // 2, img_width - x_pos))
|
104 |
+
with col2:
|
105 |
+
y_pos = st.slider("Y position (Up-Down)", 0, img_height, img_height // 4)
|
106 |
+
crop_height = st.slider("Height", 10, img_height - y_pos, min(img_height // 2, img_height - y_pos))
|
107 |
+
|
108 |
+
preview_image = draw_crop_preview(original_image.copy(), x_pos, y_pos, crop_width, crop_height)
|
109 |
+
st.image(preview_image, caption="Crop Preview", use_column_width=True)
|
110 |
+
|
111 |
+
# Step 3: Set Card Parameters
|
112 |
+
st.divider()
|
113 |
+
st.subheader("⚙️ Step 3: Set Your Festive Border Design with Flux + Fireworks!")
|
114 |
+
for i in range(4):
|
115 |
+
with st.expander(f"Holiday Card {i + 1} Parameters"):
|
116 |
+
card_params = st.session_state.card_params[i]
|
117 |
+
|
118 |
+
# Set default values for card parameters if not already set
|
119 |
+
card_params.setdefault("prompt", holiday_border_prompts[i % len(holiday_border_prompts)]) # Set default from holiday prompts
|
120 |
+
card_params.setdefault("guidance_scale", default_guidance_scale)
|
121 |
+
card_params.setdefault("num_inference_steps", default_num_inference_steps)
|
122 |
+
card_params.setdefault("seed", i * 100)
|
123 |
+
|
124 |
+
selected_prompt = st.selectbox(f"Choose a holiday-themed prompt for Holiday Card {i + 1}", options=["Custom"] + holiday_border_prompts)
|
125 |
+
custom_prompt = st.text_input(f"Enter custom prompt for Holiday Card {i + 1}", value=card_params["prompt"]) if selected_prompt == "Custom" else selected_prompt
|
126 |
+
|
127 |
+
# Allow the user to tweak other parameters
|
128 |
+
guidance_scale = st.slider(
|
129 |
+
f"Guidance Scale for Holiday Card {i + 1}",
|
130 |
+
min_value=0.0, max_value=20.0, value=card_params["guidance_scale"], step=0.1
|
131 |
+
)
|
132 |
+
num_inference_steps = st.slider(
|
133 |
+
f"Number of Inference Steps for Holiday Card {i + 1}",
|
134 |
+
min_value=1, max_value=100, value=card_params["num_inference_steps"], step=1
|
135 |
+
)
|
136 |
+
seed = st.slider(
|
137 |
+
f"Random Seed for Holiday Card {i + 1}",
|
138 |
+
min_value=0, max_value=1000, value=card_params["seed"]
|
139 |
+
)
|
140 |
+
|
141 |
+
st.session_state.card_params[i] = {
|
142 |
+
"prompt": custom_prompt,
|
143 |
+
"guidance_scale": guidance_scale,
|
144 |
+
"num_inference_steps": num_inference_steps,
|
145 |
+
"seed": seed
|
146 |
+
}
|
147 |
+
|
148 |
+
# Generate Holiday Cards
|
149 |
+
st.divider()
|
150 |
+
st.subheader("Preview and Share the Holiday Cheer! 🎅📬")
|
151 |
+
st.markdown("""
|
152 |
+
Click "Generate Image" and watch the magic happen! Your holiday card is just moments away from spreading joy to everyone on your list. 🎄🎁✨
|
153 |
+
""")
|
154 |
+
|
155 |
+
# Disable the generate button if the API key is missing
|
156 |
+
if not fireworks_api_key or fireworks_api_key.strip() == "":
|
157 |
+
st.warning("Enter a valid Fireworks API key to enable card generation.")
|
158 |
+
generate_button = st.button("Generate Holiday Cards", disabled=True)
|
159 |
+
else:
|
160 |
+
generate_button = st.button("Generate Holiday Cards")
|
161 |
+
|
162 |
+
if generate_button:
|
163 |
+
with st.spinner("Processing..."):
|
164 |
+
cols = st.columns(4)
|
165 |
+
image_files = []
|
166 |
+
metadata = []
|
167 |
+
|
168 |
+
for i, params in enumerate(st.session_state.card_params):
|
169 |
+
# Generate image using Flux API with the next largest valid aspect ratio
|
170 |
+
generated_image = generate_flux_image(
|
171 |
+
model_path="flux-1-schnell-fp8",
|
172 |
+
prompt=params['prompt'],
|
173 |
+
steps=params['num_inference_steps'],
|
174 |
+
guidance_scale=params['guidance_scale'],
|
175 |
+
seed=params['seed'],
|
176 |
+
api_key=fireworks_api_key,
|
177 |
+
aspect_ratio=f"{aspect_ratio[0]}:{aspect_ratio[1]}" # Ensure aspect ratio is passed as a string in "width:height" format
|
178 |
+
)
|
179 |
+
|
180 |
+
if generated_image:
|
181 |
+
generated_image = generated_image.resize(original_image.size)
|
182 |
+
|
183 |
+
# Center the cropped original image onto the generated image
|
184 |
+
cropped_original = original_image.crop((x_pos, y_pos, x_pos + crop_width, y_pos + crop_height))
|
185 |
+
flux_width, flux_height = generated_image.size
|
186 |
+
cropped_width, cropped_height = cropped_original.size
|
187 |
+
center_x = (flux_width - cropped_width) // 2
|
188 |
+
center_y = (flux_height - cropped_height) // 2
|
189 |
+
final_image = generated_image.copy()
|
190 |
+
final_image.paste(cropped_original, (center_x, center_y))
|
191 |
+
|
192 |
+
# Save final image and metadata
|
193 |
+
img_byte_arr = BytesIO()
|
194 |
+
final_image.save(img_byte_arr, format="PNG")
|
195 |
+
img_byte_arr.seek(0)
|
196 |
+
image_files.append((f"holiday_card_{i + 1}.png", img_byte_arr))
|
197 |
+
|
198 |
+
metadata.append({
|
199 |
+
"Card": f"Holiday Card {i + 1}",
|
200 |
+
"Prompt": params['prompt'],
|
201 |
+
"Guidance Scale": params['guidance_scale'],
|
202 |
+
"Inference Steps": params['num_inference_steps'],
|
203 |
+
"Seed": params['seed']
|
204 |
+
})
|
205 |
+
|
206 |
+
st.session_state.generated_cards[i] = {
|
207 |
+
"image": final_image,
|
208 |
+
"metadata": metadata[-1]
|
209 |
+
}
|
210 |
+
|
211 |
+
# Display the final holiday card
|
212 |
+
cols[i].image(final_image, caption=f"Holiday Card {i + 1}", use_column_width=True)
|
213 |
+
cols[i].write(f"**Prompt:** {params['prompt']}")
|
214 |
+
cols[i].write(f"**Guidance Scale:** {params['guidance_scale']}")
|
215 |
+
cols[i].write(f"**Inference Steps:** {params['num_inference_steps']}")
|
216 |
+
cols[i].write(f"**Seed:** {params['seed']}")
|
217 |
+
else:
|
218 |
+
st.error(f"Failed to generate holiday card {i + 1}. Please try again.")
|
219 |
+
|
220 |
+
# Create the ZIP file with all images and metadata
|
221 |
+
if image_files:
|
222 |
+
zip_buffer = BytesIO()
|
223 |
+
with zipfile.ZipFile(zip_buffer, "w") as zf:
|
224 |
+
for file_name, img_data in image_files:
|
225 |
+
zf.writestr(file_name, img_data.getvalue())
|
226 |
+
|
227 |
+
metadata_str = "\n\n".join([f"{m['Card']}:\nPrompt: {m['Prompt']}\nGuidance Scale: {m['Guidance Scale']}\nInference Steps: {m['Inference Steps']}\nSeed: {m['Seed']}" for m in metadata])
|
228 |
+
zf.writestr("metadata.txt", metadata_str)
|
229 |
+
|
230 |
+
zip_buffer.seek(0)
|
231 |
+
|
232 |
+
# Single download button for all images and metadata
|
233 |
+
st.download_button(
|
234 |
+
label="Download all images and metadata as ZIP",
|
235 |
+
data=zip_buffer,
|
236 |
+
file_name="holiday_cards.zip",
|
237 |
+
mime="application/zip"
|
238 |
+
)
|
239 |
+
|
240 |
+
|
241 |
+
# Footer Section
|
242 |
+
st.divider()
|
243 |
+
st.markdown(
|
244 |
+
"""
|
245 |
+
Thank you for using the Holiday Card Generator powered by **Fireworks**! 🎉
|
246 |
+
Share your creations with the world and spread the holiday cheer!
|
247 |
+
Happy Holidays from the **Fireworks Team**. 💥
|
248 |
+
"""
|
249 |
+
)
|
pages/3_Customize_Holiday_Borders.py
CHANGED
@@ -1,348 +1,244 @@
|
|
1 |
import streamlit as st
|
2 |
-
import cv2
|
3 |
-
import requests
|
4 |
-
from io import BytesIO
|
5 |
-
from PIL import Image, ImageDraw, ImageFont, ImageFile
|
6 |
-
import numpy as np
|
7 |
import os
|
8 |
-
from
|
|
|
9 |
import zipfile
|
|
|
10 |
|
11 |
-
#
|
12 |
-
|
|
|
|
|
|
|
13 |
|
14 |
-
#
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
# Folder to store generated images
|
20 |
-
GENERATED_IMAGES_DIR = "generated_images"
|
21 |
-
os.makedirs(GENERATED_IMAGES_DIR, exist_ok=True)
|
22 |
-
|
23 |
-
if not api_key:
|
24 |
-
st.error("API key not found. Make sure FIREWORKS_API_KEY is set in the .env file.")
|
25 |
-
st.stop()
|
26 |
-
|
27 |
-
VALID_ASPECT_RATIOS = {
|
28 |
-
(1, 1): "1:1", (21, 9): "21:9", (16, 9): "16:9", (3, 2): "3:2", (5, 4): "5:4",
|
29 |
-
(4, 5): "4:5", (2, 3): "2:3", (9, 16): "9:16", (9, 21): "9:21",
|
30 |
-
}
|
31 |
-
|
32 |
-
def get_closest_aspect_ratio(width, height):
|
33 |
-
aspect_ratio = width / height
|
34 |
-
closest_ratio = min(VALID_ASPECT_RATIOS.keys(), key=lambda x: abs((x[0] / x[1]) - aspect_ratio))
|
35 |
-
return VALID_ASPECT_RATIOS[closest_ratio]
|
36 |
-
|
37 |
-
def process_image(uploaded_image):
|
38 |
-
image = np.array(Image.open(uploaded_image).convert('L'))
|
39 |
-
edges = cv2.Canny(image, 100, 200)
|
40 |
-
edges_rgb = cv2.cvtColor(edges, cv2.COLOR_GRAY2RGB)
|
41 |
-
pil_image = Image.fromarray(edges_rgb)
|
42 |
-
byte_arr = BytesIO()
|
43 |
-
pil_image.save(byte_arr, format='JPEG')
|
44 |
-
byte_arr.seek(0)
|
45 |
-
return byte_arr, pil_image
|
46 |
-
|
47 |
-
def call_control_net_api(uploaded_image, prompt, control_mode=0, guidance_scale=3.5, num_inference_steps=30, seed=0, controlnet_conditioning_scale=1.0):
|
48 |
-
control_image, processed_image = process_image(uploaded_image)
|
49 |
-
files = {'control_image': ('control_image.jpg', control_image, 'image/jpeg')}
|
50 |
-
original_image = Image.open(uploaded_image)
|
51 |
-
width, height = original_image.size
|
52 |
-
aspect_ratio = get_closest_aspect_ratio(width, height)
|
53 |
-
data = {
|
54 |
-
'prompt': prompt,
|
55 |
-
'control_mode': control_mode,
|
56 |
-
'aspect_ratio': aspect_ratio,
|
57 |
-
'guidance_scale': guidance_scale,
|
58 |
-
'num_inference_steps': num_inference_steps,
|
59 |
-
'seed': seed,
|
60 |
-
'controlnet_conditioning_scale': controlnet_conditioning_scale
|
61 |
-
}
|
62 |
-
headers = {
|
63 |
-
'accept': 'image/jpeg',
|
64 |
-
'authorization': f'Bearer {api_key}',
|
65 |
-
}
|
66 |
-
response = requests.post('https://api.fireworks.ai/inference/v1/workflows/accounts/fireworks/models/flux-1-dev-controlnet-union/control_net',
|
67 |
-
files=files, data=data, headers=headers)
|
68 |
-
if response.status_code == 200:
|
69 |
-
return Image.open(BytesIO(response.content)), processed_image, original_image
|
70 |
-
else:
|
71 |
-
st.error(f"Request failed with status code: {response.status_code}, Response: {response.text}")
|
72 |
-
return None, None, None
|
73 |
-
|
74 |
-
def draw_crop_preview(image, x, y, width, height):
|
75 |
-
draw = ImageDraw.Draw(image)
|
76 |
-
draw.rectangle([x, y, x + width, y + height], outline="red", width=2)
|
77 |
-
return image
|
78 |
-
|
79 |
-
def add_message_to_image(image, cropped_bottom_y, message, font_path, font_size):
|
80 |
-
draw = ImageDraw.Draw(image)
|
81 |
-
|
82 |
-
# Try to load the selected font file with user-defined size
|
83 |
-
try:
|
84 |
-
font = ImageFont.truetype(font_path, font_size)
|
85 |
-
st.write(f"Loaded font from {font_path} with font size {font_size}")
|
86 |
-
except IOError as e:
|
87 |
-
st.error(f"Error loading font: {e}. Loading default font.")
|
88 |
-
font = ImageFont.load_default()
|
89 |
-
|
90 |
-
# Calculate the text size dynamically
|
91 |
-
text_bbox = draw.textbbox((0, 0), message, font=font)
|
92 |
-
text_width = text_bbox[2] - text_bbox[0]
|
93 |
-
text_height = text_bbox[3] - text_bbox[1]
|
94 |
-
st.write(f"Text width: {text_width}, Text height: {text_height}")
|
95 |
-
|
96 |
-
# Calculate the space between the bottom of the cropped image and the bottom of the card
|
97 |
-
bottom_space = image.height - cropped_bottom_y
|
98 |
-
|
99 |
-
# Place the text box centered along the Y axis in the bottom space
|
100 |
-
y_position = cropped_bottom_y + (bottom_space - text_height) // 2
|
101 |
-
|
102 |
-
# Position the text box at the center of the width of the image
|
103 |
-
x_position = (image.width - text_width) // 2
|
104 |
-
|
105 |
-
# Define the padding for the text box
|
106 |
-
padding_x = 50 # Adjust the padding width as needed
|
107 |
-
padding_y = 50 # Adjust the padding height as needed
|
108 |
-
|
109 |
-
# Define the text box (background) coordinates with padding
|
110 |
-
box_coords = [
|
111 |
-
x_position - padding_x, y_position - padding_y,
|
112 |
-
x_position + text_width + padding_x, y_position + text_height + padding_y
|
113 |
-
]
|
114 |
-
|
115 |
-
# Draw a white rectangle as the background for the text box
|
116 |
-
draw.rectangle(box_coords, fill="white")
|
117 |
-
|
118 |
-
# Draw the text in black on top of the white background
|
119 |
-
draw.text((x_position, y_position), message, font=font, fill="black")
|
120 |
-
|
121 |
-
return image
|
122 |
-
|
123 |
-
# Define the fonts you want to allow
|
124 |
-
fonts = {
|
125 |
-
"DejaVu Sans Bold": "./fonts/dejavu-sans-bold.ttf",
|
126 |
-
"Covered By You": "./fonts/CoveredByYourGrace-Regular.ttf", # Update this path to your actual font paths
|
127 |
-
"Julee Regular": "./fonts/Julee-Regular.ttf", # Example font
|
128 |
-
"Kalam Regular": "./fonts/Kalam-Regular.ttf",
|
129 |
-
"Knewave Regular": "./fonts/Knewave-Regular.ttf",
|
130 |
-
"Sancreek Regular": "./fonts/Sancreek-Regular.ttf",
|
131 |
-
"Vast Shadow Regular": "./fonts/VastShadow-Regular.ttf",
|
132 |
-
}
|
133 |
-
|
134 |
-
# Fireworks Logo at the top
|
135 |
-
logo_image = Image.open("img/fireworksai_logo.png")
|
136 |
-
st.image(logo_image)
|
137 |
-
|
138 |
-
st.title("🎨 Holiday Card Generator - Part B: Finalize Your Holiday Card")
|
139 |
|
140 |
-
|
141 |
-
|
|
|
142 |
|
143 |
-
|
144 |
-
|
145 |
-
2. **Add a Personalized Message**: Write your holiday greeting and customize it with font and size options to match your style.
|
146 |
-
3. **Review & Download**: Preview your finished card and download it for sharing.
|
147 |
|
148 |
-
**
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
|
153 |
-
|
154 |
""")
|
155 |
|
156 |
-
|
157 |
-
st.divider()
|
158 |
-
|
159 |
-
# Initialize session state variables if they don't exist
|
160 |
if 'uploaded_file' not in st.session_state:
|
161 |
st.session_state.uploaded_file = None
|
162 |
if 'generated_image' not in st.session_state:
|
163 |
st.session_state.generated_image = None
|
|
|
|
|
|
|
|
|
|
|
164 |
|
165 |
-
#
|
166 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
167 |
st.markdown("""
|
168 |
-
|
169 |
-
-
|
170 |
-
|
|
|
171 |
uploaded_file = st.file_uploader("Upload an image", type=["png", "jpg", "jpeg"])
|
172 |
if uploaded_file is not None:
|
173 |
-
st.session_state.uploaded_file = uploaded_file
|
174 |
-
st.divider()
|
175 |
|
176 |
-
#
|
177 |
if st.session_state.uploaded_file is not None:
|
178 |
try:
|
179 |
original_image = Image.open(st.session_state.uploaded_file)
|
180 |
-
st.subheader("Your uploaded image")
|
181 |
st.image(original_image, caption="Uploaded Image", use_column_width=True)
|
182 |
|
183 |
-
|
184 |
-
st.
|
185 |
-
|
186 |
-
st.subheader("✂️ Step 2: Crop It Like It's Hot!")
|
187 |
st.markdown("""
|
188 |
-
|
189 |
-
|
190 |
-
|
|
|
191 |
col1, col2 = st.columns(2)
|
192 |
with col1:
|
193 |
-
x_pos = st.slider("X position (Left-Right)", 0, img_width, img_width // 4
|
194 |
-
|
195 |
-
crop_width = st.slider("Crop Width", 10, img_width - x_pos, min(img_width // 2, img_width - x_pos),
|
196 |
-
help="Adjust the width of the crop.")
|
197 |
with col2:
|
198 |
-
y_pos = st.slider("Y position (Up-Down)", 0, img_height, img_height // 4
|
199 |
-
|
200 |
-
crop_height = st.slider("Crop Height", 10, img_height - y_pos, min(img_height // 2, img_height - y_pos),
|
201 |
-
help="Adjust the height of the crop.")
|
202 |
|
203 |
preview_image = draw_crop_preview(original_image.copy(), x_pos, y_pos, crop_width, crop_height)
|
204 |
st.image(preview_image, caption="Crop Preview", use_column_width=True)
|
205 |
|
206 |
-
|
207 |
-
|
208 |
-
st.divider()
|
209 |
-
holiday_prompts = [
|
210 |
-
"A border of Festive snowflakes and winter patterns for a holiday card",
|
211 |
-
"A border of Joyful Christmas ornaments and lights decorating the edges",
|
212 |
-
"A border of Warm and cozy fireplace scene with stockings and garlands",
|
213 |
-
"A border of Colorful Hanukkah menorahs and dreidels along the border",
|
214 |
-
"A border of New Year's Eve fireworks with stars and confetti framing the image",
|
215 |
-
"A border of Samhain symbols with Celtic knots, pumpkins, and candles for a mystical October celebration",
|
216 |
-
"A border of Día de Muertos skulls (calaveras), marigold flowers, and candles in vibrant colors",
|
217 |
-
"A border of Yule imagery with holly, pine trees, and Norse winter symbols under the starry night sky",
|
218 |
-
"A border of Diwali lamps (diyas), rangoli patterns, and lotus flowers for a bright and colorful festival",
|
219 |
-
"A border of Dongzhi Festival glutinous rice balls and cozy winter imagery, celebrating family gatherings",
|
220 |
-
"A border of Soyal dancers, sun symbols, and traditional Zuni and Hopi winter motifs to welcome the sun's return",
|
221 |
-
"A border of Winter Solstice symbols with bare trees, stars, and the moon against a cold winter night",
|
222 |
-
"A border of spooky Halloween elements like pumpkins, bats, ghosts, and cobwebs for a haunted theme",
|
223 |
-
"A border of fall harvest items like pumpkins, cornucopias, autumn leaves, and turkeys",
|
224 |
-
"A border of Christmas ornaments, wreaths, and lights for a joyful, festive holiday look"
|
225 |
-
]
|
226 |
-
st.subheader("⚙️ Step 3: Design Your Festive Border with Flux + Fireworks!")
|
227 |
st.markdown("""
|
228 |
-
|
229 |
-
|
230 |
-
|
|
|
231 |
custom_prompt = st.text_input("Enter your custom prompt") if selected_prompt == "Custom" else ""
|
232 |
prompt = custom_prompt if selected_prompt == "Custom" else selected_prompt
|
233 |
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
guidance_scale = st.slider("Guidance Scale", min_value=0.0, max_value=20.0, value=3.5, step=0.1)
|
238 |
-
num_inference_steps = st.slider("Number of Inference Steps", min_value=1, max_value=100, value=30, step=1)
|
239 |
-
seed = st.slider("Random Seed", min_value=0, max_value=1000, value=0)
|
240 |
|
241 |
if st.button("Generate Holiday Card"):
|
242 |
if not prompt.strip():
|
243 |
st.error("Please enter a prompt.")
|
244 |
else:
|
245 |
-
with st.spinner("
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
|
|
|
|
|
|
|
|
|
|
250 |
)
|
251 |
-
|
252 |
if generated_image is not None:
|
253 |
-
# Resize generated_image to match original_image size
|
254 |
generated_image = generated_image.resize(original_image.size)
|
255 |
-
|
256 |
-
# Create a copy of the generated image
|
257 |
-
final_image = generated_image.copy()
|
258 |
-
|
259 |
-
# Crop the selected portion of the original image
|
260 |
cropped_original = original_image.crop((x_pos, y_pos, x_pos + crop_width, y_pos + crop_height))
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
# Calculate the center of the generated image
|
266 |
-
center_x = (final_image.width - cropped_width) // 2
|
267 |
-
center_y = (final_image.height - cropped_height) // 2
|
268 |
-
|
269 |
-
# Paste the cropped portion of the original image onto the generated image at the calculated center
|
270 |
final_image.paste(cropped_original, (center_x, center_y))
|
271 |
-
|
272 |
-
# Save the generated image in session state for persistence
|
273 |
st.session_state.generated_image = final_image
|
274 |
|
275 |
-
# Save
|
276 |
-
|
|
|
|
|
|
|
|
|
|
|
277 |
|
278 |
-
|
279 |
-
st.image(final_image, caption="Final Holiday Card", use_column_width=True)
|
280 |
-
else:
|
281 |
-
st.error("Failed to generate holiday card. Please try again.")
|
282 |
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
st.markdown("""
|
288 |
-
- Add a fun, heartfelt, or witty holiday message to the bottom of your card. 🎉
|
289 |
-
- Choose from a variety of **festive fonts** and adjust the **font size** to make your message stand out! 🎅 """)
|
290 |
|
291 |
-
|
292 |
-
|
|
|
|
|
|
|
|
|
|
|
293 |
|
294 |
-
|
|
|
|
|
295 |
selected_font = st.selectbox("Choose a font for your message", options=list(fonts.keys()))
|
296 |
-
|
297 |
-
# Slider to
|
298 |
-
|
299 |
-
|
300 |
-
# Get the corresponding font path
|
301 |
font_path = fonts[selected_font]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
302 |
|
303 |
if st.button("Generate Holiday Card with Message"):
|
304 |
final_image_with_message = st.session_state.generated_image.copy()
|
305 |
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
312 |
|
313 |
-
|
314 |
-
st.image(final_image_with_message, caption="Holiday Card with Custom Message", use_column_width=True)
|
315 |
|
316 |
-
# Save image
|
317 |
img_byte_arr = BytesIO()
|
318 |
final_image_with_message.save(img_byte_arr, format="PNG")
|
319 |
img_byte_arr.seek(0)
|
320 |
|
321 |
-
#
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
327 |
st.download_button(
|
328 |
-
label="Download Holiday Card
|
329 |
-
data=
|
330 |
-
file_name="holiday_card_with_message.
|
331 |
-
mime="
|
332 |
)
|
333 |
|
334 |
-
# Save image locally
|
335 |
-
final_image_with_message.save(f"{GENERATED_IMAGES_DIR}/holiday_card_with_message.png", format="PNG")
|
336 |
except OSError:
|
337 |
st.error("The uploaded image seems to be truncated or corrupted. Please try with a different image.")
|
338 |
else:
|
339 |
st.warning("Please upload an image to get started.")
|
340 |
|
341 |
-
# Load and display previously generated images
|
342 |
-
st.divider()
|
343 |
-
st.subheader("Previously Generated Holiday Cards")
|
344 |
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import streamlit as st
|
|
|
|
|
|
|
|
|
|
|
2 |
import os
|
3 |
+
from PIL import Image
|
4 |
+
from io import BytesIO
|
5 |
import zipfile
|
6 |
+
from dotenv import load_dotenv
|
7 |
|
8 |
+
# Import from helper_utilities.py
|
9 |
+
from utils.helper_utilities import (
|
10 |
+
get_next_largest_aspect_ratio, draw_crop_preview,
|
11 |
+
add_custom_message, generate_flux_image
|
12 |
+
)
|
13 |
|
14 |
+
# Import from configuration.py
|
15 |
+
from utils.configuration import (
|
16 |
+
default_guidance_scale, default_num_inference_steps,
|
17 |
+
default_seed, holiday_border_prompts, example_holiday_messages, fonts
|
18 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
|
20 |
+
# Streamlit app starts here
|
21 |
+
st.image("img/fireworksai_logo.png")
|
22 |
+
st.title("🎄 Holiday Card Customizer 🎅")
|
23 |
|
24 |
+
st.markdown("""
|
25 |
+
✨ Welcome to the **Holiday Card Generator**! Get ready to sprinkle some holiday magic onto your custom card with a festive border and a heartfelt message. Here's how you can spread the holiday cheer:
|
|
|
|
|
26 |
|
27 |
+
1. 🎁 **Upload your image** – Select a photo or design to be the star of your holiday card.
|
28 |
+
2. ✨ **Design your holiday card border** – Choose from seasonal prompts or create your own unique design.
|
29 |
+
3. 💌 **Add your personal holiday message** – Make it meaningful, funny, or warm-hearted!
|
30 |
+
4. 📦 **Download your finished card** – Ready to send to friends and family.
|
31 |
|
32 |
+
Let’s bring your holiday greetings to life! 🎉
|
33 |
""")
|
34 |
|
35 |
+
# Initialize session state variables
|
|
|
|
|
|
|
36 |
if 'uploaded_file' not in st.session_state:
|
37 |
st.session_state.uploaded_file = None
|
38 |
if 'generated_image' not in st.session_state:
|
39 |
st.session_state.generated_image = None
|
40 |
+
if 'metadata' not in st.session_state:
|
41 |
+
st.session_state.metadata = {}
|
42 |
+
|
43 |
+
st.divider()
|
44 |
+
st.subheader("🎅 Step 1: Load Your Fireworks API Key 🎄")
|
45 |
|
46 |
+
# Load API Key
|
47 |
+
# Define and ensure the .env directory and file exist
|
48 |
+
dotenv_path = os.path.join(os.path.dirname(__file__), '..', 'env', '.env')
|
49 |
+
os.makedirs(os.path.dirname(dotenv_path), exist_ok=True)
|
50 |
+
|
51 |
+
# Create the .env file if it doesn't exist
|
52 |
+
if not os.path.exists(dotenv_path):
|
53 |
+
with open(dotenv_path, "w") as f:
|
54 |
+
st.success(f"Created {dotenv_path}")
|
55 |
+
|
56 |
+
# Load environment variables from the .env file
|
57 |
+
load_dotenv(dotenv_path, override=True)
|
58 |
+
|
59 |
+
# Check if the Fireworks API key is set or blank
|
60 |
+
fireworks_api_key = os.getenv("FIREWORKS_API_KEY")
|
61 |
+
|
62 |
+
# Show the entire app but disable running parts if no API key
|
63 |
+
if not fireworks_api_key or fireworks_api_key.strip() == "":
|
64 |
+
fireworks_api_key = st.text_input("Enter Fireworks API Key", type="password")
|
65 |
+
|
66 |
+
# Optionally, allow the user to save the API key to the .env file
|
67 |
+
if fireworks_api_key and st.checkbox("Save API key for future use"):
|
68 |
+
with open(dotenv_path, "a") as f:
|
69 |
+
f.write(f"FIREWORKS_API_KEY={fireworks_api_key}\n")
|
70 |
+
st.success("API key saved to .env file.")
|
71 |
+
else:
|
72 |
+
st.success(f"API key loaded successfully: partial preview {fireworks_api_key[:5]}")
|
73 |
+
|
74 |
+
# Step 1: Upload Image
|
75 |
+
st.subheader("🎁 Step 2: Upload Your Festive Picture 🎨")
|
76 |
st.markdown("""
|
77 |
+
It's time to pick your image! This will be the centerpiece of your holiday card.
|
78 |
+
- **Pro Tip**: Choose something that brings out the warmth and joy of the season! 🎄🎅
|
79 |
+
Upload a **PNG** or **JPEG** image to get started.
|
80 |
+
""")
|
81 |
uploaded_file = st.file_uploader("Upload an image", type=["png", "jpg", "jpeg"])
|
82 |
if uploaded_file is not None:
|
83 |
+
st.session_state.uploaded_file = uploaded_file
|
|
|
84 |
|
85 |
+
# Process uploaded image
|
86 |
if st.session_state.uploaded_file is not None:
|
87 |
try:
|
88 |
original_image = Image.open(st.session_state.uploaded_file)
|
|
|
89 |
st.image(original_image, caption="Uploaded Image", use_column_width=True)
|
90 |
|
91 |
+
# Step 2: Crop Image
|
92 |
+
st.subheader("✂️ Step 3: Trim Your Image Just Right! 🎄")
|
|
|
|
|
93 |
st.markdown("""
|
94 |
+
Adjust the crop sliders to capture the most festive part of your image.
|
95 |
+
A preview will show you how your holiday card is shaping up! ✨
|
96 |
+
""")
|
97 |
+
img_width, img_height = original_image.size
|
98 |
col1, col2 = st.columns(2)
|
99 |
with col1:
|
100 |
+
x_pos = st.slider("X position (Left-Right)", 0, img_width, img_width // 4)
|
101 |
+
crop_width = st.slider("Width", 10, img_width - x_pos, min(img_width // 2, img_width - x_pos))
|
|
|
|
|
102 |
with col2:
|
103 |
+
y_pos = st.slider("Y position (Up-Down)", 0, img_height, img_height // 4)
|
104 |
+
crop_height = st.slider("Height", 10, img_height - y_pos, min(img_height // 2, img_height - y_pos))
|
|
|
|
|
105 |
|
106 |
preview_image = draw_crop_preview(original_image.copy(), x_pos, y_pos, crop_width, crop_height)
|
107 |
st.image(preview_image, caption="Crop Preview", use_column_width=True)
|
108 |
|
109 |
+
# Step 3: Set Border Parameters and Generate Image
|
110 |
+
st.subheader("🎨 Step 4: Design Your Holiday Border! 🎅")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
111 |
st.markdown("""
|
112 |
+
Choose from a range of holiday themes like snowflakes, cozy fireplaces, or fireworks!
|
113 |
+
Want to create your own magical scene? Enter a **custom prompt** to make it truly unique! 🎄
|
114 |
+
""")
|
115 |
+
selected_prompt = st.selectbox("Choose a holiday-themed prompt or enter your own", options=["Custom"] + holiday_border_prompts)
|
116 |
custom_prompt = st.text_input("Enter your custom prompt") if selected_prompt == "Custom" else ""
|
117 |
prompt = custom_prompt if selected_prompt == "Custom" else selected_prompt
|
118 |
|
119 |
+
guidance_scale = st.slider("Guidance Scale", min_value=0.0, max_value=20.0, value=default_guidance_scale, step=0.1)
|
120 |
+
num_inference_steps = st.slider("Number of Inference Steps", min_value=1, max_value=100, value=default_num_inference_steps, step=1)
|
121 |
+
seed = st.slider("Random Seed", min_value=0, max_value=1000, value=default_seed)
|
|
|
|
|
|
|
122 |
|
123 |
if st.button("Generate Holiday Card"):
|
124 |
if not prompt.strip():
|
125 |
st.error("Please enter a prompt.")
|
126 |
else:
|
127 |
+
with st.spinner("Adding holiday magic..."):
|
128 |
+
aspect_ratio = get_next_largest_aspect_ratio(img_width, img_height)
|
129 |
+
generated_image = generate_flux_image(
|
130 |
+
model_path="flux-1-schnell-fp8",
|
131 |
+
api_key=fireworks_api_key,
|
132 |
+
prompt=prompt,
|
133 |
+
steps=num_inference_steps,
|
134 |
+
guidance_scale=guidance_scale,
|
135 |
+
seed=seed,
|
136 |
+
aspect_ratio=f"{aspect_ratio[0]}:{aspect_ratio[1]}"
|
137 |
)
|
138 |
+
|
139 |
if generated_image is not None:
|
|
|
140 |
generated_image = generated_image.resize(original_image.size)
|
|
|
|
|
|
|
|
|
|
|
141 |
cropped_original = original_image.crop((x_pos, y_pos, x_pos + crop_width, y_pos + crop_height))
|
142 |
+
center_x = (generated_image.width - crop_width) // 2
|
143 |
+
center_y = (generated_image.height - crop_height) // 2
|
144 |
+
final_image = generated_image.copy()
|
|
|
|
|
|
|
|
|
|
|
|
|
145 |
final_image.paste(cropped_original, (center_x, center_y))
|
|
|
|
|
146 |
st.session_state.generated_image = final_image
|
147 |
|
148 |
+
# Save metadata
|
149 |
+
st.session_state.metadata = {
|
150 |
+
"Prompt": prompt,
|
151 |
+
"Guidance Scale": guidance_scale,
|
152 |
+
"Inference Steps": num_inference_steps,
|
153 |
+
"Seed": seed
|
154 |
+
}
|
155 |
|
156 |
+
st.image(final_image, caption="🎁 Final Holiday Card 🎁", use_column_width=True)
|
|
|
|
|
|
|
157 |
|
158 |
+
# Print the metadata below the image
|
159 |
+
st.markdown("### ✨ Card Metadata")
|
160 |
+
for key, value in st.session_state.metadata.items():
|
161 |
+
st.write(f"**{key}:** {value}")
|
|
|
|
|
|
|
162 |
|
163 |
+
# Step 4: Add Custom Message
|
164 |
+
if 'generated_image' in st.session_state and st.session_state.generated_image is not None:
|
165 |
+
st.subheader("💌 Step 5: Add Your Heartfelt Holiday Message! 🎅")
|
166 |
+
st.markdown("""
|
167 |
+
Select a pre-written festive message or craft your own!
|
168 |
+
Customize the font, size, and color to make it pop! 🌟
|
169 |
+
""")
|
170 |
|
171 |
+
selected_message = st.selectbox("Choose an example holiday message or write your own", options=["Custom"] + example_holiday_messages)
|
172 |
+
custom_message = st.text_input("Enter your holiday message") if selected_message == "Custom" else selected_message
|
173 |
+
|
174 |
selected_font = st.selectbox("Choose a font for your message", options=list(fonts.keys()))
|
175 |
+
font_size = st.slider("Adjust font size", min_value=1, max_value=500, value=100, step=5)
|
176 |
+
# Slider to choose the max characters per line for text wrapping
|
177 |
+
max_chars = st.slider("Max characters per line:", min_value=20, max_value=100, value=40)
|
|
|
|
|
178 |
font_path = fonts[selected_font]
|
179 |
+
bg_color = st.color_picker("Pick a background color for the text", "#FFFFFF")
|
180 |
+
font_color = st.color_picker("Pick a font color", "#000000")
|
181 |
+
alpha = st.slider("Background transparency (0: fully transparent, 255: fully opaque)", 0, 255, 200)
|
182 |
+
position_vertical = st.radio("Vertical position", ["Top", "Center", "Bottom"])
|
183 |
+
position_horizontal = st.radio("Horizontal position", ["Left", "Center", "Right"])
|
184 |
+
|
185 |
+
|
186 |
|
187 |
if st.button("Generate Holiday Card with Message"):
|
188 |
final_image_with_message = st.session_state.generated_image.copy()
|
189 |
|
190 |
+
final_image_with_message = add_custom_message(
|
191 |
+
final_image_with_message,
|
192 |
+
custom_message,
|
193 |
+
font_path,
|
194 |
+
font_size,
|
195 |
+
position_vertical,
|
196 |
+
position_horizontal,
|
197 |
+
max_chars=max_chars,
|
198 |
+
bg_color=bg_color,
|
199 |
+
font_color=font_color,
|
200 |
+
alpha=alpha
|
201 |
+
)
|
202 |
|
203 |
+
st.image(final_image_with_message, caption="🎄 Holiday Card with Custom Message 🎄", use_column_width=True)
|
|
|
204 |
|
205 |
+
# Save image and metadata to zip
|
206 |
img_byte_arr = BytesIO()
|
207 |
final_image_with_message.save(img_byte_arr, format="PNG")
|
208 |
img_byte_arr.seek(0)
|
209 |
|
210 |
+
# Create a zip file containing the image and metadata
|
211 |
+
zip_buffer = BytesIO()
|
212 |
+
with zipfile.ZipFile(zip_buffer, "w") as zf:
|
213 |
+
# Add the image to the zip
|
214 |
+
zf.writestr("holiday_card_with_message.png", img_byte_arr.getvalue())
|
215 |
+
|
216 |
+
# Add the metadata as a text file to the zip
|
217 |
+
metadata_str = "\n".join([f"{key}: {value}" for key, value in st.session_state.metadata.items()])
|
218 |
+
zf.writestr("metadata.txt", metadata_str)
|
219 |
+
|
220 |
+
zip_buffer.seek(0)
|
221 |
+
|
222 |
+
# Step 5: Download as zip
|
223 |
st.download_button(
|
224 |
+
label="🎁 Download Holiday Card and Metadata as ZIP 🎁",
|
225 |
+
data=zip_buffer,
|
226 |
+
file_name="holiday_card_with_message.zip",
|
227 |
+
mime="application/zip"
|
228 |
)
|
229 |
|
|
|
|
|
230 |
except OSError:
|
231 |
st.error("The uploaded image seems to be truncated or corrupted. Please try with a different image.")
|
232 |
else:
|
233 |
st.warning("Please upload an image to get started.")
|
234 |
|
|
|
|
|
|
|
235 |
|
236 |
+
# Footer Section
|
237 |
+
st.divider()
|
238 |
+
st.markdown(
|
239 |
+
"""
|
240 |
+
Thank you for using the Holiday Card Generator powered by **Fireworks**! 🎉
|
241 |
+
Share your creations with the world and spread the holiday cheer!
|
242 |
+
Happy Holidays from the **Fireworks Team**. 💥
|
243 |
+
"""
|
244 |
+
)
|
pages/4_Morph_Holiday_Card.py
DELETED
@@ -1,268 +0,0 @@
|
|
1 |
-
import streamlit as st
|
2 |
-
import cv2
|
3 |
-
import requests
|
4 |
-
from io import BytesIO
|
5 |
-
from PIL import Image, ImageDraw, ImageFont, ImageFilter
|
6 |
-
import numpy as np
|
7 |
-
import os
|
8 |
-
from dotenv import load_dotenv
|
9 |
-
|
10 |
-
# Load environment variables
|
11 |
-
dotenv_path = os.path.join(os.path.dirname(__file__), '../env/.env')
|
12 |
-
load_dotenv(dotenv_path, override=True)
|
13 |
-
api_key = os.getenv("FIREWORKS_API_KEY")
|
14 |
-
|
15 |
-
if not api_key:
|
16 |
-
st.error("API key not found. Make sure FIREWORKS_API_KEY is set in the .env file.")
|
17 |
-
st.stop()
|
18 |
-
|
19 |
-
# Initialize session state variables if they don't exist
|
20 |
-
if 'uploaded_file' not in st.session_state:
|
21 |
-
st.session_state.uploaded_file = None
|
22 |
-
if 'generated_image' not in st.session_state:
|
23 |
-
st.session_state.generated_image = None
|
24 |
-
if 'control_image' not in st.session_state:
|
25 |
-
st.session_state.control_image = None
|
26 |
-
|
27 |
-
VALID_ASPECT_RATIOS = {
|
28 |
-
(1, 1): "1:1", (21, 9): "21:9", (16, 9): "16:9", (3, 2): "3:2", (5, 4): "5:4",
|
29 |
-
(4, 5): "4:5", (2, 3): "2:3", (9, 16): "9:16", (9, 21): "9:21",
|
30 |
-
}
|
31 |
-
|
32 |
-
def get_closest_aspect_ratio(width, height):
|
33 |
-
aspect_ratio = width / height
|
34 |
-
closest_ratio = min(VALID_ASPECT_RATIOS.keys(), key=lambda x: abs((x[0] / x[1]) - aspect_ratio))
|
35 |
-
return closest_ratio, VALID_ASPECT_RATIOS[closest_ratio]
|
36 |
-
|
37 |
-
def add_padding_to_aspect_ratio(image, target_aspect_ratio):
|
38 |
-
width, height = image.size
|
39 |
-
target_width, target_height = target_aspect_ratio
|
40 |
-
|
41 |
-
if width / height > target_width / target_height:
|
42 |
-
new_height = int(width * target_height / target_width)
|
43 |
-
new_width = width
|
44 |
-
else:
|
45 |
-
new_width = int(height * target_width / target_height)
|
46 |
-
new_height = height
|
47 |
-
|
48 |
-
# Create a new image with the target aspect ratio and paste the original image in the center
|
49 |
-
padded_image = Image.new('RGB', (new_width, new_height), (128, 128, 128)) # Grey padding
|
50 |
-
paste_x = (new_width - width) // 2
|
51 |
-
paste_y = (new_height - height) // 2
|
52 |
-
padded_image.paste(image, (paste_x, paste_y))
|
53 |
-
|
54 |
-
return padded_image
|
55 |
-
|
56 |
-
def create_control_image(original_image, x_pos, y_pos, crop_width, crop_height):
|
57 |
-
# Crop the selected area
|
58 |
-
cropped_area = original_image.crop((x_pos, y_pos, x_pos + crop_width, y_pos + crop_height))
|
59 |
-
|
60 |
-
# Add 20% padding around the cropped area
|
61 |
-
pad_width = int(crop_width * 0.2)
|
62 |
-
pad_height = int(crop_height * 0.2)
|
63 |
-
padded_width = crop_width + 2 * pad_width
|
64 |
-
padded_height = crop_height + 2 * pad_height
|
65 |
-
padded_image = Image.new('RGB', (padded_width, padded_height), (128, 128, 128)) # Grey padding
|
66 |
-
padded_image.paste(cropped_area, (pad_width, pad_height))
|
67 |
-
|
68 |
-
# Adjust the padded image to match the closest valid aspect ratio
|
69 |
-
closest_aspect_ratio, _ = get_closest_aspect_ratio(padded_width, padded_height)
|
70 |
-
control_image = add_padding_to_aspect_ratio(padded_image, closest_aspect_ratio)
|
71 |
-
|
72 |
-
return control_image
|
73 |
-
|
74 |
-
def process_image(image):
|
75 |
-
gray_image = image.convert('L')
|
76 |
-
np_image = np.array(gray_image)
|
77 |
-
edges = cv2.Canny(np_image, 100, 200)
|
78 |
-
edges_rgb = cv2.cvtColor(edges, cv2.COLOR_GRAY2RGB)
|
79 |
-
edge_image = Image.fromarray(edges_rgb)
|
80 |
-
byte_arr = BytesIO()
|
81 |
-
edge_image.save(byte_arr, format='JPEG')
|
82 |
-
byte_arr.seek(0)
|
83 |
-
return byte_arr, edge_image
|
84 |
-
|
85 |
-
def call_control_net_api(control_image, prompt, control_mode=0, guidance_scale=3.5, num_inference_steps=30, seed=0, controlnet_conditioning_scale=1.0):
|
86 |
-
processed_image_bytes, processed_image = process_image(control_image)
|
87 |
-
files = {'control_image': ('control_image.jpg', processed_image_bytes, 'image/jpeg')}
|
88 |
-
|
89 |
-
width, height = control_image.size
|
90 |
-
aspect_ratio = get_closest_aspect_ratio(width, height)[1]
|
91 |
-
|
92 |
-
data = {
|
93 |
-
'prompt': prompt,
|
94 |
-
'control_mode': control_mode,
|
95 |
-
'aspect_ratio': aspect_ratio,
|
96 |
-
'guidance_scale': guidance_scale,
|
97 |
-
'num_inference_steps': num_inference_steps,
|
98 |
-
'seed': seed,
|
99 |
-
'controlnet_conditioning_scale': controlnet_conditioning_scale
|
100 |
-
}
|
101 |
-
|
102 |
-
headers = {
|
103 |
-
'accept': 'image/jpeg',
|
104 |
-
'authorization': f'Bearer {api_key}',
|
105 |
-
}
|
106 |
-
|
107 |
-
response = requests.post(
|
108 |
-
'https://api.fireworks.ai/inference/v1/workflows/accounts/fireworks/models/flux-1-dev-controlnet-union/control_net',
|
109 |
-
files=files, data=data, headers=headers
|
110 |
-
)
|
111 |
-
|
112 |
-
if response.status_code == 200:
|
113 |
-
return Image.open(BytesIO(response.content)), processed_image
|
114 |
-
else:
|
115 |
-
st.error(f"Request failed with status code: {response.status_code}, Response: {response.text}")
|
116 |
-
return None, None
|
117 |
-
|
118 |
-
def draw_crop_preview(image, x, y, width, height):
|
119 |
-
draw = ImageDraw.Draw(image)
|
120 |
-
for i in range(5): # Draw multiple rectangles for thickness
|
121 |
-
draw.rectangle([x+i, y+i, x+width-i, y+height-i], outline="red")
|
122 |
-
return image
|
123 |
-
|
124 |
-
def smooth_edges(image, crop_box, blur_radius=5):
|
125 |
-
# Create a mask for the cropped area
|
126 |
-
mask = Image.new("L", image.size, 0)
|
127 |
-
draw = ImageDraw.Draw(mask)
|
128 |
-
draw.rectangle(crop_box, fill=255)
|
129 |
-
mask = mask.filter(ImageFilter.GaussianBlur(blur_radius))
|
130 |
-
|
131 |
-
# Create a new image for blending
|
132 |
-
blended_image = Image.composite(image, Image.new("RGB", image.size, (0, 0, 0)), mask)
|
133 |
-
return blended_image
|
134 |
-
|
135 |
-
def add_custom_message(image, message, font_path, font_size):
|
136 |
-
# Load a font
|
137 |
-
try:
|
138 |
-
font = ImageFont.truetype(font_path, font_size)
|
139 |
-
except IOError:
|
140 |
-
font = ImageFont.load_default()
|
141 |
-
|
142 |
-
draw = ImageDraw.Draw(image)
|
143 |
-
|
144 |
-
# Calculate the text bounding box dynamically
|
145 |
-
text_bbox = draw.textbbox((0, 0), message, font=font)
|
146 |
-
text_width = text_bbox[2] - text_bbox[0]
|
147 |
-
text_height = text_bbox[3] - text_bbox[1]
|
148 |
-
img_width, img_height = image.size
|
149 |
-
|
150 |
-
# Center the text
|
151 |
-
x_pos = (img_width - text_width) // 2
|
152 |
-
y_pos = img_height - text_height - 10 # 10 px padding from the bottom
|
153 |
-
|
154 |
-
# Define the padding for the white box
|
155 |
-
padding = 10
|
156 |
-
|
157 |
-
# Draw a white rectangle (the background box) behind the text
|
158 |
-
draw.rectangle([x_pos - padding, y_pos - padding, x_pos + text_width + padding, y_pos + text_height + padding], fill="white")
|
159 |
-
|
160 |
-
# Draw the black text on top of the white box
|
161 |
-
draw.text((x_pos, y_pos), message, font=font, fill="black")
|
162 |
-
|
163 |
-
return image
|
164 |
-
|
165 |
-
|
166 |
-
# Define available fonts
|
167 |
-
fonts = {
|
168 |
-
"DejaVu Sans Bold": "./fonts/dejavu-sans-bold.ttf",
|
169 |
-
"Covered By You": "./fonts/CoveredByYourGrace-Regular.ttf", # Update this path to your actual font paths
|
170 |
-
"Julee Regular": "./fonts/Julee-Regular.ttf", # Example font
|
171 |
-
"Kalam Regular": "./fonts/Kalam-Regular.ttf",
|
172 |
-
"Knewave Regular": "./fonts/Knewave-Regular.ttf",
|
173 |
-
"Sancreek Regular": "./fonts/Sancreek-Regular.ttf",
|
174 |
-
"Vast Shadow Regular": "./fonts/VastShadow-Regular.ttf",
|
175 |
-
}
|
176 |
-
|
177 |
-
# Streamlit UI
|
178 |
-
st.title("Holiday Card Generator with ControlNet")
|
179 |
-
|
180 |
-
uploaded_file = st.file_uploader("Upload an image", type=["png", "jpg", "jpeg"])
|
181 |
-
|
182 |
-
if uploaded_file is not None:
|
183 |
-
st.session_state.uploaded_file = uploaded_file # Save the file to session state
|
184 |
-
original_image = Image.open(st.session_state.uploaded_file)
|
185 |
-
st.image(original_image, caption="Uploaded Image", use_column_width=True)
|
186 |
-
|
187 |
-
img_width, img_height = original_image.size
|
188 |
-
|
189 |
-
st.subheader("Crop Selection")
|
190 |
-
col1, col2 = st.columns(2)
|
191 |
-
with col1:
|
192 |
-
x_pos = st.slider("X position", 0, img_width, img_width // 4)
|
193 |
-
crop_width = st.slider("Width", 10, img_width - x_pos, min(img_width // 2, img_width - x_pos))
|
194 |
-
with col2:
|
195 |
-
y_pos = st.slider("Y position", 0, img_height, img_height // 4)
|
196 |
-
crop_height = st.slider("Height", 10, img_height - y_pos, min(img_height // 2, img_height - y_pos))
|
197 |
-
|
198 |
-
preview_image = draw_crop_preview(original_image.copy(), x_pos, y_pos, crop_width, crop_height)
|
199 |
-
st.image(preview_image, caption="Crop Preview", use_column_width=True)
|
200 |
-
|
201 |
-
holiday_prompts = [
|
202 |
-
"A portrait of a person framed by festive snowflakes and winter patterns, with soft, frosty details along the edges for a holiday card",
|
203 |
-
"A joyful portrait with Christmas ornaments and twinkling lights decorating the borders, framing the person in a warm, festive glow",
|
204 |
-
"A cozy portrait with a fireplace scene, stockings, and garlands along the edges, enhancing the warmth of the person in the center",
|
205 |
-
"A colorful Hanukkah-themed portrait with menorahs, dreidels, and holiday lights framing the person against a rich backdrop",
|
206 |
-
"A New Year's Eve portrait with celebratory fireworks, stars, and confetti surrounding the person, giving an energetic, celebratory vibe",
|
207 |
-
"A mystical portrait framed by Samhain symbols like Celtic knots, pumpkins, and candles, creating an enchanting October atmosphere",
|
208 |
-
"A vibrant Día de Muertos portrait with colorful skulls (calaveras), marigold flowers, and candles framing the subject with traditional flair",
|
209 |
-
"A Yule-themed portrait framed by holly, pine trees, and Norse winter symbols under a starry sky, bringing out the warmth of the subject",
|
210 |
-
"A bright and joyful Diwali portrait with lamps (diyas), intricate rangoli patterns, and lotus flowers framing the person for a festive look",
|
211 |
-
"A cozy winter portrait with Dongzhi Festival glutinous rice balls and family gathering symbols along the edges, giving a homely, warm feeling",
|
212 |
-
"A spiritual portrait framed by Zuni and Hopi winter motifs, including Soyal dancers and sun symbols, welcoming the return of the sun",
|
213 |
-
"A winter solstice portrait with bare trees, stars, and the moon softly framing the person in a serene, cold night atmosphere",
|
214 |
-
"A spooky Halloween-themed portrait framed by pumpkins, bats, ghosts, and cobwebs, adding a playful, haunting edge to the subject",
|
215 |
-
"A harvest-themed portrait surrounded by pumpkins, cornucopias, autumn leaves, and turkeys, capturing the essence of Thanksgiving",
|
216 |
-
"A joyful Christmas portrait framed by ornaments, wreaths, and glowing lights, creating a festive and heartwarming holiday look"
|
217 |
-
]
|
218 |
-
|
219 |
-
selected_prompt = st.selectbox("Choose a holiday-themed prompt or enter your own", options=["Custom"] + holiday_prompts)
|
220 |
-
custom_prompt = st.text_input("Enter your custom prompt") if selected_prompt == "Custom" else ""
|
221 |
-
prompt = custom_prompt if selected_prompt == "Custom" else selected_prompt
|
222 |
-
|
223 |
-
with st.expander("Advanced Parameters"):
|
224 |
-
control_mode = st.slider("Control Mode", min_value=0, max_value=2, value=0)
|
225 |
-
controlnet_conditioning_scale = st.slider("ControlNet Conditioning Scale", min_value=0.0, max_value=1.0, value=0.5, step=0.1)
|
226 |
-
guidance_scale = st.slider("Guidance Scale", min_value=0.0, max_value=20.0, value=3.5, step=0.1)
|
227 |
-
num_inference_steps = st.slider("Number of Inference Steps", min_value=1, max_value=100, value=30, step=1)
|
228 |
-
seed = st.slider("Random Seed", min_value=0, max_value=1000, value=0)
|
229 |
-
|
230 |
-
custom_message = st.text_input("Enter a custom holiday message to add to the card")
|
231 |
-
|
232 |
-
# Font selection dropdown and font size slider
|
233 |
-
selected_font = st.selectbox("Choose a font for your message", options=list(fonts.keys()))
|
234 |
-
font_size = st.slider("Font size", 10, 100, 40)
|
235 |
-
|
236 |
-
if st.button("Generate Flux Image"):
|
237 |
-
if not prompt.strip():
|
238 |
-
st.error("Please enter a prompt.")
|
239 |
-
else:
|
240 |
-
with st.spinner("Generating Flux image..."):
|
241 |
-
control_image = create_control_image(original_image, x_pos, y_pos, crop_width, crop_height)
|
242 |
-
st.image(control_image, caption="Control Image (Cropped and Padded)", use_column_width=True)
|
243 |
-
|
244 |
-
generated_image, processed_image = call_control_net_api(
|
245 |
-
control_image, prompt, control_mode=control_mode,
|
246 |
-
guidance_scale=guidance_scale, num_inference_steps=num_inference_steps,
|
247 |
-
seed=seed, controlnet_conditioning_scale=controlnet_conditioning_scale
|
248 |
-
)
|
249 |
-
|
250 |
-
if generated_image is not None:
|
251 |
-
st.session_state.generated_image = generated_image
|
252 |
-
st.session_state.control_image = control_image
|
253 |
-
|
254 |
-
# Add custom message if provided
|
255 |
-
if custom_message:
|
256 |
-
font_path = fonts[selected_font]
|
257 |
-
generated_image_with_message = add_custom_message(generated_image.copy(), custom_message, font_path, font_size)
|
258 |
-
st.image(generated_image_with_message, caption="Generated Flux Image with Message", use_column_width=True)
|
259 |
-
else:
|
260 |
-
st.image(generated_image, caption="Generated Flux Image", use_column_width=True)
|
261 |
-
|
262 |
-
st.success("Flux image generated successfully!")
|
263 |
-
else:
|
264 |
-
st.error("Failed to generate image. Please try again.")
|
265 |
-
|
266 |
-
else:
|
267 |
-
st.warning("Please upload an image to get started.")
|
268 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_endpoint.py
CHANGED
@@ -5,7 +5,8 @@ from PIL import Image
|
|
5 |
from io import BytesIO
|
6 |
|
7 |
# Correct the path to the .env file to reflect its location
|
8 |
-
dotenv_path = os.path.join(os.path.dirname(__file__), '
|
|
|
9 |
|
10 |
# Load environment variables from the .env file
|
11 |
load_dotenv(dotenv_path, override=True)
|
@@ -26,8 +27,8 @@ if not prompt.strip():
|
|
26 |
# Set the model endpoint for either flux-1-dev or flux-1-schnell
|
27 |
# For dev: "flux-1-dev" (30 steps)
|
28 |
# For schnell: "flux-1-schnell" (4 steps)
|
29 |
-
model_path = "flux-1-schnell"
|
30 |
-
|
31 |
|
32 |
# API URL for the model
|
33 |
url = f"https://api.fireworks.ai/inference/v1/workflows/accounts/fireworks/models/{model_path}/text_to_image"
|
|
|
5 |
from io import BytesIO
|
6 |
|
7 |
# Correct the path to the .env file to reflect its location
|
8 |
+
dotenv_path = os.path.join(os.path.dirname(__file__), 'env', '.env')
|
9 |
+
#print("dotenv_path: ", dotenv_path)
|
10 |
|
11 |
# Load environment variables from the .env file
|
12 |
load_dotenv(dotenv_path, override=True)
|
|
|
27 |
# Set the model endpoint for either flux-1-dev or flux-1-schnell
|
28 |
# For dev: "flux-1-dev" (30 steps)
|
29 |
# For schnell: "flux-1-schnell" (4 steps)
|
30 |
+
#model_path = "flux-1-schnell-fp8"
|
31 |
+
model_path = "flux-1-dev-fp8" # Uncomment if you want to switch to the dev model
|
32 |
|
33 |
# API URL for the model
|
34 |
url = f"https://api.fireworks.ai/inference/v1/workflows/accounts/fireworks/models/{model_path}/text_to_image"
|
utils/__init__.py
ADDED
File without changes
|
utils/configuration.py
ADDED
@@ -0,0 +1,185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# configuration.py
|
2 |
+
|
3 |
+
import os
|
4 |
+
|
5 |
+
|
6 |
+
# ----------------------------------------------
|
7 |
+
# Default Parameters for Image Generation
|
8 |
+
# ----------------------------------------------
|
9 |
+
|
10 |
+
# ControlNet and Image Generation defaults
|
11 |
+
default_control_mode = 0
|
12 |
+
default_guidance_scale = 3.5 # Adjust how strictly the model follows the prompt
|
13 |
+
default_num_inference_steps = 30 # Number of inference steps for image generation
|
14 |
+
default_seed = 0 # Seed for randomization (useful for reproducibility)
|
15 |
+
default_controlnet_conditioning_scale = 0.5 # Influence scale for ControlNet
|
16 |
+
|
17 |
+
# ----------------------------------------------
|
18 |
+
# Font and Color Defaults
|
19 |
+
# ----------------------------------------------
|
20 |
+
|
21 |
+
# Default font sizes and colors for image captions
|
22 |
+
default_font_size = 40 # Base font size for captions
|
23 |
+
default_max_font_size = 300 # Maximum allowable font size
|
24 |
+
default_min_font_size = 10 # Minimum allowable font size
|
25 |
+
background_color = "#FFFFFF" # Default background color for captions
|
26 |
+
font_color = "#000000" # Default font color for captions
|
27 |
+
|
28 |
+
# ----------------------------------------------
|
29 |
+
# API URL Configuration
|
30 |
+
# ----------------------------------------------
|
31 |
+
|
32 |
+
# Templates for FLUX and ControlNet API URLs (parameterized to allow flexibility)
|
33 |
+
flux_model_url_template = "https://api.fireworks.ai/inference/v1/workflows/accounts/fireworks/models/{model_path}/text_to_image"
|
34 |
+
control_net_url = "https://api.fireworks.ai/inference/v1/workflows/accounts/fireworks/models/flux-1-dev-controlnet-union/control_net"
|
35 |
+
|
36 |
+
# ----------------------------------------------
|
37 |
+
# API Headers Configuration
|
38 |
+
# ----------------------------------------------
|
39 |
+
|
40 |
+
# Function to generate headers for FLUX requests
|
41 |
+
def get_headers(api_key):
|
42 |
+
return {
|
43 |
+
"Authorization": f"Bearer {api_key}", # Bearer token for API authentication
|
44 |
+
"Content-Type": "application/json", # Requests will send/receive JSON
|
45 |
+
"Accept": "image/jpeg" # Expect images as the response format
|
46 |
+
}
|
47 |
+
|
48 |
+
# Function to generate headers for ControlNet requests
|
49 |
+
def get_control_net_headers(api_key):
|
50 |
+
return {
|
51 |
+
"accept": "image/jpeg", # Expecting an image in response
|
52 |
+
"authorization": f"Bearer {api_key}", # API Key for authentication
|
53 |
+
}
|
54 |
+
|
55 |
+
# ----------------------------------------------
|
56 |
+
# Fonts Configuration
|
57 |
+
# ----------------------------------------------
|
58 |
+
# Get the base directory dynamically (root project directory)
|
59 |
+
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
60 |
+
|
61 |
+
# Define the fonts you want to allow, with paths relative to BASE_DIR
|
62 |
+
fonts = {
|
63 |
+
"DejaVu Sans Bold": os.path.join(BASE_DIR, "fonts", "dejavu-sans-bold.ttf"),
|
64 |
+
"Covered By You": os.path.join(BASE_DIR, "fonts", "CoveredByYourGrace-Regular.ttf"),
|
65 |
+
"Julee Regular": os.path.join(BASE_DIR, "fonts", "Julee-Regular.ttf"),
|
66 |
+
"Kalam Regular": os.path.join(BASE_DIR, "fonts", "Kalam-Regular.ttf"),
|
67 |
+
"Knewave Regular": os.path.join(BASE_DIR, "fonts", "Knewave-Regular.ttf"),
|
68 |
+
"Sancreek Regular": os.path.join(BASE_DIR, "fonts", "Sancreek-Regular.ttf"),
|
69 |
+
"Vast Shadow Regular": os.path.join(BASE_DIR, "fonts", "VastShadow-Regular.ttf")
|
70 |
+
}
|
71 |
+
|
72 |
+
# ----------------------------------------------
|
73 |
+
# Aspect Ratios Configuration
|
74 |
+
# ----------------------------------------------
|
75 |
+
|
76 |
+
# Dictionary of valid aspect ratios for generated images
|
77 |
+
valid_aspect_ratios = {
|
78 |
+
(1, 1): "1:1", # Square
|
79 |
+
(21, 9): "21:9", # Widescreen
|
80 |
+
(16, 9): "16:9", # Standard video
|
81 |
+
(3, 2): "3:2", # Common photo ratio
|
82 |
+
(5, 4): "5:4", # Almost square
|
83 |
+
(4, 5): "4:5", # Portrait
|
84 |
+
(2, 3): "2:3", # Another portrait ratio
|
85 |
+
(9, 16): "9:16" # Vertical video
|
86 |
+
}
|
87 |
+
|
88 |
+
# ----------------------------------------------
|
89 |
+
# Holiday-Themed Prompts
|
90 |
+
# ----------------------------------------------
|
91 |
+
|
92 |
+
# List of holiday scene prompts for generating themed images
|
93 |
+
holiday_scene_prompts = [
|
94 |
+
"A peaceful winter landscape covered in fresh snow, with delicate snowflakes drifting through the air and soft, frosty patterns along the edges, creating a serene holiday card",
|
95 |
+
"A cozy Christmas scene featuring a warmly lit cabin in the woods, surrounded by snow-covered trees and twinkling holiday lights, with a sky glowing softly in the background",
|
96 |
+
"A fireplace with stockings, garlands, and a glowing hearth, set in a cozy room decorated for the holidays, capturing the warmth and tranquility of the season",
|
97 |
+
"A colorful Hanukkah celebration with glowing menorahs, dreidels, and shimmering holiday lights illuminating the night, set against a rich, deep blue backdrop for a festive scene",
|
98 |
+
"A vibrant New Year's Eve cityscape with bright fireworks exploding in the sky, colorful confetti drifting through the air, and stars twinkling above the skyline, radiating excitement",
|
99 |
+
"A mystical Samhain forest scene, with pumpkins glowing softly among tall trees, and a full moon casting light over an ancient Celtic stone circle, evoking an air of magic and mystery",
|
100 |
+
"A lively Día de Muertos altar adorned with colorful calaveras, bright marigold flowers, and glowing candles, set against a traditional Mexican backdrop, celebrating the holiday’s joy",
|
101 |
+
"A wintery Yule forest scene under a starry sky, with snow-covered pine trees, holly bushes, and traditional Norse winter symbols decorating the landscape, capturing the warmth of the season",
|
102 |
+
"A bright and vibrant Diwali celebration with glowing diyas lining a courtyard, intricate rangoli patterns on the ground, and colorful fireworks lighting up the night sky",
|
103 |
+
"A serene Kwanzaa scene featuring a kinara with glowing candles, surrounded by decorations in red, black, and green, set in a peaceful and reflective atmosphere",
|
104 |
+
"A bountiful Thanksgiving table scene set outdoors, surrounded by autumn leaves, pumpkins, and a golden sunset, capturing the essence of gratitude and togetherness",
|
105 |
+
"A tranquil Winter Solstice night with a glowing full moon above, bare trees, stars twinkling in the sky, and a peaceful blanket of snow covering the quiet landscape",
|
106 |
+
"A festive St. Patrick's Day scene featuring a green countryside, with shamrocks covering the rolling hills, a rainbow stretching across the sky, and a pot of gold in the distance",
|
107 |
+
"A colorful Lunar New Year street scene with red lanterns hanging from the rooftops, dragon dancers parading down the street, and vibrant fireworks lighting up the night",
|
108 |
+
"A peaceful Easter garden filled with blooming spring flowers, pastel-colored eggs hidden among the grass, and soft sunlight streaming through the trees",
|
109 |
+
"A romantic Valentine's Day scene with glowing candles, heart-shaped decorations hanging from tree branches, and a soft pink and red sunset in the sky",
|
110 |
+
"A lively Holi celebration in an open field, with bursts of vibrant color powder filling the air, and people joyfully celebrating under a bright blue sky",
|
111 |
+
"A tranquil Ramadan evening scene with a crescent moon and stars hanging in the night sky, glowing lanterns decorating the streets, and peaceful reflections along a quiet river",
|
112 |
+
"A lively Independence Day celebration with colorful fireworks lighting up the night sky, flags waving in the breeze, and a peaceful lakeside reflecting the celebration"
|
113 |
+
]
|
114 |
+
|
115 |
+
|
116 |
+
# List of holiday scene prompts for generating themed images
|
117 |
+
holiday_portrait_prompts = [
|
118 |
+
"A portrait of a person framed by festive snowflakes and winter patterns, with soft, frosty details along the edges for a holiday card",
|
119 |
+
"A joyful portrait with Christmas ornaments and twinkling lights decorating the borders, framing the person in a warm, festive glow",
|
120 |
+
"A cozy portrait with a fireplace scene, stockings, and garlands along the edges, enhancing the warmth of the person in the center",
|
121 |
+
"A colorful Hanukkah-themed portrait with menorahs, dreidels, and holiday lights framing the person against a rich backdrop",
|
122 |
+
"A New Year's Eve portrait with celebratory fireworks, stars, and confetti surrounding the person, giving an energetic, celebratory vibe",
|
123 |
+
"A mystical portrait framed by Samhain symbols like Celtic knots, pumpkins, and candles, creating an enchanting October atmosphere",
|
124 |
+
"A vibrant Día de Muertos portrait with colorful skulls (calaveras), marigold flowers, and candles framing the subject with traditional flair",
|
125 |
+
"A Yule-themed portrait framed by holly, pine trees, and Norse winter symbols under a starry sky, bringing out the warmth of the subject",
|
126 |
+
"A bright and joyful Diwali portrait with lamps (diyas), intricate rangoli patterns, and lotus flowers framing the person for a festive look",
|
127 |
+
"A cozy winter portrait with Dongzhi Festival glutinous rice balls and family gathering symbols along the edges, giving a homely, warm feeling",
|
128 |
+
"A spiritual portrait framed by Zuni and Hopi winter motifs, including Soyal dancers and sun symbols, welcoming the return of the sun",
|
129 |
+
"A winter solstice portrait with bare trees, stars, and the moon softly framing the person in a serene, cold night atmosphere",
|
130 |
+
"A spooky Halloween-themed portrait framed by pumpkins, bats, ghosts, and cobwebs, adding a playful, haunting edge to the subject",
|
131 |
+
"A harvest-themed portrait surrounded by pumpkins, cornucopias, autumn leaves, and turkeys, capturing the essence of Thanksgiving",
|
132 |
+
"A joyful Christmas portrait framed by ornaments, wreaths, and glowing lights, creating a festive and heartwarming holiday look"
|
133 |
+
]
|
134 |
+
|
135 |
+
# List of prompts for holiday cards
|
136 |
+
holiday_border_prompts = [
|
137 |
+
"A holiday card with a blank center, surrounded by a delicate border of falling snowflakes and frosty winter patterns along the edges, creating a peaceful winter scene",
|
138 |
+
"A cozy holiday card with a blank center, bordered by snow-covered pine trees, glowing cabin lights, and twinkling Christmas decorations framing the edges",
|
139 |
+
"A Lunar New Year holiday card with a blank center, surrounded by red lanterns, dragon dancers, and vibrant fireworks along the edges, celebrating the new year",
|
140 |
+
"An Easter holiday card with a blank center, bordered by blooming spring flowers, pastel-colored eggs, and soft sunlight, capturing the joy of new beginnings along the edges",
|
141 |
+
"A Valentine's Day holiday card with a blank center, surrounded by heart-shaped decorations, glowing candles, and pink and red floral patterns for a romantic atmosphere",
|
142 |
+
"A Holi holiday card with a blank center, framed by bursts of vibrant color powder and festive energy, capturing the lively spirit of the festival along the edges",
|
143 |
+
"A Ramadan holiday card with a blank center, bordered by a crescent moon, twinkling stars, and glowing lanterns, creating a peaceful and reflective holiday frame",
|
144 |
+
"An Independence Day holiday card with a blank center, framed by colorful fireworks, waving flags, and festive red, white, and blue decorations along the edges",
|
145 |
+
"A warm holiday card with a blank center, bordered by stockings, garlands, and a glowing hearth, evoking the coziness of a holiday fireplace",
|
146 |
+
"A Hanukkah holiday card with a blank center, surrounded by a glowing menorah, dreidels, and shimmering holiday lights along the edges, set against a deep blue background",
|
147 |
+
"A New Year's Eve holiday card with a blank center, framed by bright fireworks, colorful confetti, and twinkling stars, capturing the festive excitement around the edges",
|
148 |
+
"A mystical Samhain holiday card with a blank center, bordered by glowing pumpkins, Celtic knots, and a full moon shining through bare trees, creating an air of mystery",
|
149 |
+
"A Día de Muertos holiday card with a blank center, bordered by vibrant calaveras (skulls), bright marigolds, and glowing candles, celebrating the Mexican holiday along the edges",
|
150 |
+
"A Yule holiday card with a blank center, bordered by snow-covered pine trees, holly bushes, and traditional Norse winter symbols, creating a festive winter atmosphere",
|
151 |
+
"A Diwali holiday card with a blank center, framed by glowing diyas, intricate rangoli patterns, and colorful fireworks, lighting up the edges with festive energy",
|
152 |
+
"A Kwanzaa holiday card with a blank center, surrounded by a kinara with glowing candles and decorations in red, black, and green, creating a reflective and peaceful frame",
|
153 |
+
"A Thanksgiving holiday card with a blank center, bordered by autumn leaves, pumpkins, and a golden sunset, evoking the warmth and gratitude of the harvest season",
|
154 |
+
"A Winter Solstice holiday card with a blank center, framed by a glowing full moon, twinkling stars, and bare trees dusted with snow, creating a tranquil winter scene",
|
155 |
+
"A St. Patrick's Day holiday card with a blank center, bordered by shamrocks, a rainbow, and a pot of gold, framed by lush green fields for a festive Irish celebration",
|
156 |
+
]
|
157 |
+
|
158 |
+
|
159 |
+
|
160 |
+
# ----------------------------------------------
|
161 |
+
# Example Holiday Messages
|
162 |
+
# ----------------------------------------------
|
163 |
+
|
164 |
+
# List of example holiday messages to pair with the scene prompts
|
165 |
+
example_holiday_messages = [
|
166 |
+
"Wishing you peace and serenity this winter season. May the snow bring you joy and tranquility!",
|
167 |
+
"May your Christmas be as cozy and warm as a cabin in the woods, filled with holiday lights and love.",
|
168 |
+
"Warm wishes for the holidays! May your hearth and home be filled with love and joy this season.",
|
169 |
+
"Happy Hanukkah! May the light of the menorah shine brightly and bring joy to your heart.",
|
170 |
+
"Wishing you a vibrant New Year's Eve filled with excitement, fireworks, and joy for the year ahead!",
|
171 |
+
"May the magic and mystery of Samhain fill your spirit with wonder. Have a mystical holiday!",
|
172 |
+
"Celebrating the joy of Día de Muertos! May the memories of your loved ones bring peace and happiness.",
|
173 |
+
"Wishing you a Yule season filled with the warmth of the Norse tradition and the beauty of snowy forests.",
|
174 |
+
"Happy Diwali! May the lights of the diyas and the colors of rangoli bring you prosperity and joy.",
|
175 |
+
"Wishing you a reflective and peaceful Kwanzaa. May your home be filled with love, light, and unity.",
|
176 |
+
"Happy Thanksgiving! May your table be filled with gratitude, and your heart with the warmth of togetherness.",
|
177 |
+
"Wishing you a peaceful Winter Solstice night under the full moon. May the season bring you reflection and rest.",
|
178 |
+
"May the luck of the Irish be with you! Wishing you a joyful St. Patrick's Day filled with rainbows and gold.",
|
179 |
+
"Happy Lunar New Year! May the dragon dancers bring joy, and the fireworks light up a prosperous new year.",
|
180 |
+
"Wishing you a peaceful Easter surrounded by blooming flowers and the warmth of springtime sunshine.",
|
181 |
+
"Happy Valentine's Day! May your day be filled with love, glowing candles, and heartwarming moments.",
|
182 |
+
"Wishing you a joyful Holi! May the colors of the festival fill your life with vibrancy and happiness.",
|
183 |
+
"Ramadan Mubarak! May the crescent moon bring you peace, and the lanterns light up your spiritual path.",
|
184 |
+
"Happy Independence Day! May the fireworks light up the sky, and may freedom continue to inspire us all."
|
185 |
+
]
|
utils/helper_utilities.py
ADDED
@@ -0,0 +1,281 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# helper_utilities.py
|
2 |
+
|
3 |
+
import os
|
4 |
+
from PIL import Image, ImageDraw, ImageFont, ImageColor
|
5 |
+
import requests
|
6 |
+
from io import BytesIO
|
7 |
+
import textwrap
|
8 |
+
import cv2
|
9 |
+
import numpy as np
|
10 |
+
from dotenv import load_dotenv
|
11 |
+
|
12 |
+
# Import configuration values from configuration.py
|
13 |
+
from utils.configuration import (
|
14 |
+
fonts, # Now this should correctly import fonts
|
15 |
+
default_guidance_scale,
|
16 |
+
default_control_mode,
|
17 |
+
default_num_inference_steps,
|
18 |
+
default_seed,
|
19 |
+
default_controlnet_conditioning_scale,
|
20 |
+
flux_model_url_template,
|
21 |
+
control_net_url,
|
22 |
+
get_headers,
|
23 |
+
get_control_net_headers,
|
24 |
+
valid_aspect_ratios
|
25 |
+
)
|
26 |
+
|
27 |
+
# ----------------------------------------------
|
28 |
+
# Environment Handling
|
29 |
+
# ----------------------------------------------
|
30 |
+
|
31 |
+
def load_env(dotenv_path):
|
32 |
+
"""Loads environment variables from a .env file."""
|
33 |
+
load_dotenv(dotenv_path, override=True)
|
34 |
+
|
35 |
+
# ----------------------------------------------
|
36 |
+
# General Utilities
|
37 |
+
# ----------------------------------------------
|
38 |
+
|
39 |
+
def get_font(font_path, font_size):
|
40 |
+
"""Tries to load a specified font. Falls back to default if not found."""
|
41 |
+
try:
|
42 |
+
font = ImageFont.truetype(font_path, font_size)
|
43 |
+
except IOError:
|
44 |
+
font = ImageFont.load_default()
|
45 |
+
return font
|
46 |
+
|
47 |
+
def send_post_request(url, headers, data, files=None):
|
48 |
+
"""A general function to send POST requests and handle responses."""
|
49 |
+
if files:
|
50 |
+
response = requests.post(url, headers=headers, files=files, data=data)
|
51 |
+
else:
|
52 |
+
response = requests.post(url, headers=headers, json=data)
|
53 |
+
|
54 |
+
if response.status_code == 200:
|
55 |
+
return response
|
56 |
+
else:
|
57 |
+
raise RuntimeError(f"Request failed with status code: {response.status_code}, Response: {response.text}")
|
58 |
+
|
59 |
+
def resize_image(image, size):
|
60 |
+
"""Resizes the image to the specified size."""
|
61 |
+
return image.resize(size)
|
62 |
+
|
63 |
+
def combine_images(image1, image2):
|
64 |
+
"""Combines two images side by side."""
|
65 |
+
combined = Image.new("RGB", (image1.width + image2.width, max(image1.height, image2.height)))
|
66 |
+
combined.paste(image1, (0, 0))
|
67 |
+
combined.paste(image2, (image1.width, 0))
|
68 |
+
return combined
|
69 |
+
|
70 |
+
# ----------------------------------------------
|
71 |
+
# FLUX API and ControlNet Functions
|
72 |
+
# ----------------------------------------------
|
73 |
+
|
74 |
+
def generate_flux_image(model_path, api_key, prompt, steps=default_num_inference_steps,
|
75 |
+
aspect_ratio="16:9", guidance_scale=default_guidance_scale,
|
76 |
+
seed=default_seed, deployment=None):
|
77 |
+
"""
|
78 |
+
Generates an image using the FLUX model based on the provided parameters.
|
79 |
+
|
80 |
+
:param model_path: Path to the FLUX model
|
81 |
+
:param api_key: API key for authentication
|
82 |
+
:param prompt: Text prompt to generate the image
|
83 |
+
:param steps: Number of inference steps for the model
|
84 |
+
:param aspect_ratio: Desired aspect ratio for the output image
|
85 |
+
:param guidance_scale: How strictly the model should follow the prompt
|
86 |
+
:param seed: Seed value for randomization (for reproducibility)
|
87 |
+
:param deployment: Optional deployment string for specific model deployments
|
88 |
+
:return: Generated image as a PIL image
|
89 |
+
"""
|
90 |
+
# Build the request URL
|
91 |
+
base_url = flux_model_url_template.format(model_path=model_path)
|
92 |
+
|
93 |
+
# If a specific deployment is provided, add it to the URL as a query parameter
|
94 |
+
if deployment:
|
95 |
+
url = f"{base_url}?deployment={deployment}"
|
96 |
+
else:
|
97 |
+
url = base_url
|
98 |
+
|
99 |
+
headers = get_headers(api_key)
|
100 |
+
|
101 |
+
# Data payload for the request
|
102 |
+
data = {
|
103 |
+
"prompt": prompt,
|
104 |
+
"aspect_ratio": aspect_ratio,
|
105 |
+
"guidance_scale": guidance_scale,
|
106 |
+
"num_inference_steps": steps,
|
107 |
+
"seed": seed
|
108 |
+
}
|
109 |
+
|
110 |
+
# Send the POST request and handle the response
|
111 |
+
response = requests.post(url, headers=headers, json=data)
|
112 |
+
|
113 |
+
if response.status_code == 200:
|
114 |
+
# If the response is successful, convert the response content into an image
|
115 |
+
img = Image.open(BytesIO(response.content))
|
116 |
+
return img
|
117 |
+
else:
|
118 |
+
# Raise an error if the request fails
|
119 |
+
raise RuntimeError(f"Failed to generate image: {response.status_code}, {response.text}")
|
120 |
+
|
121 |
+
def call_control_net_api(control_image, prompt, api_key,
|
122 |
+
control_mode=0,
|
123 |
+
guidance_scale=default_guidance_scale,
|
124 |
+
num_inference_steps=default_num_inference_steps,
|
125 |
+
seed=default_seed,
|
126 |
+
controlnet_conditioning_scale=default_controlnet_conditioning_scale):
|
127 |
+
"""
|
128 |
+
Calls the ControlNet API, sending a control image and prompt.
|
129 |
+
Generates a new image based on ControlNet, processes the control image,
|
130 |
+
and handles aspect ratios.
|
131 |
+
"""
|
132 |
+
# Process control image for ControlNet
|
133 |
+
processed_image_bytes, processed_image = process_image(control_image)
|
134 |
+
files = {'control_image': ('control_image.jpg', processed_image_bytes, 'image/jpeg')}
|
135 |
+
|
136 |
+
# Calculate aspect ratio based on control image dimensions
|
137 |
+
width, height = control_image.size
|
138 |
+
aspect_ratio = f"{width}:{height}"
|
139 |
+
|
140 |
+
data = {
|
141 |
+
'prompt': prompt,
|
142 |
+
'control_mode': control_mode,
|
143 |
+
'aspect_ratio': aspect_ratio,
|
144 |
+
'guidance_scale': guidance_scale,
|
145 |
+
'num_inference_steps': num_inference_steps,
|
146 |
+
'seed': seed,
|
147 |
+
'controlnet_conditioning_scale': controlnet_conditioning_scale
|
148 |
+
}
|
149 |
+
|
150 |
+
url = control_net_url
|
151 |
+
headers = get_control_net_headers(api_key)
|
152 |
+
|
153 |
+
# Send the POST request to ControlNet API
|
154 |
+
response = send_post_request(url, headers, data, files)
|
155 |
+
|
156 |
+
# Convert the response to an image
|
157 |
+
generated_image = Image.open(BytesIO(response.content))
|
158 |
+
return generated_image, processed_image
|
159 |
+
|
160 |
+
# ----------------------------------------------
|
161 |
+
# Image Manipulation Utilities
|
162 |
+
# ----------------------------------------------
|
163 |
+
|
164 |
+
def overlay_text_on_image(image, text, font_path, font_size, position):
|
165 |
+
"""Draws text on the image at the specified position."""
|
166 |
+
draw = ImageDraw.Draw(image)
|
167 |
+
font = get_font(font_path, font_size)
|
168 |
+
draw.text(position, text, font=font, fill="black")
|
169 |
+
return image
|
170 |
+
|
171 |
+
def get_closest_aspect_ratio(width, height):
|
172 |
+
"""
|
173 |
+
Finds the closest valid aspect ratio for the given image dimensions.
|
174 |
+
Uses the valid_aspect_ratios from configuration.py.
|
175 |
+
"""
|
176 |
+
aspect_ratio = width / height
|
177 |
+
closest_ratio = min(valid_aspect_ratios.keys(), key=lambda x: abs((x[0] / x[1]) - aspect_ratio))
|
178 |
+
return valid_aspect_ratios[closest_ratio]
|
179 |
+
|
180 |
+
|
181 |
+
def get_next_largest_aspect_ratio(width, height):
|
182 |
+
"""
|
183 |
+
Finds the next largest valid aspect ratio for the given image dimensions.
|
184 |
+
Returns the aspect ratio as a tuple, formatted as (width, height).
|
185 |
+
"""
|
186 |
+
aspect_ratio = width / height
|
187 |
+
larger_ratios = [(x[0] / x[1], x) for x in valid_aspect_ratios.keys() if (x[0] / x[1]) >= aspect_ratio]
|
188 |
+
|
189 |
+
if larger_ratios:
|
190 |
+
# Return the smallest of the larger valid aspect ratios
|
191 |
+
next_largest_ratio = min(larger_ratios, key=lambda x: x[0])
|
192 |
+
return next_largest_ratio[1] # Return the tuple (width, height)
|
193 |
+
else:
|
194 |
+
# If no larger aspect ratio is found, fall back to the closest
|
195 |
+
closest_ratio = min(valid_aspect_ratios.keys(), key=lambda x: abs((x[0] / x[1]) - aspect_ratio))
|
196 |
+
return closest_ratio
|
197 |
+
|
198 |
+
|
199 |
+
|
200 |
+
def process_image(image):
|
201 |
+
"""
|
202 |
+
Processes an image by converting it to grayscale and detecting edges.
|
203 |
+
Returns the edge-detected image.
|
204 |
+
"""
|
205 |
+
gray_image = image.convert('L')
|
206 |
+
np_image = np.array(gray_image)
|
207 |
+
edges = cv2.Canny(np_image, 100, 200)
|
208 |
+
edges_rgb = cv2.cvtColor(edges, cv2.COLOR_GRAY2RGB)
|
209 |
+
return Image.fromarray(edges_rgb)
|
210 |
+
|
211 |
+
def draw_crop_preview(image, x, y, width, height):
|
212 |
+
"""Draws a red rectangle on the image to preview a crop region."""
|
213 |
+
draw = ImageDraw.Draw(image)
|
214 |
+
draw.rectangle([x, y, x + width, y + height], outline="red", width=2)
|
215 |
+
return image
|
216 |
+
|
217 |
+
def wrap_text(text, max_chars):
|
218 |
+
"""Wraps text to a specified number of characters per line."""
|
219 |
+
return "\n".join(textwrap.fill(line, width=max_chars) for line in text.split("\n"))
|
220 |
+
|
221 |
+
# ----------------------------------------------
|
222 |
+
# Text and Image Combination Utilities
|
223 |
+
# ----------------------------------------------
|
224 |
+
|
225 |
+
def add_custom_message(image, message, font_path, font_size, position_vertical, position_horizontal, max_chars, bg_color, font_color, alpha):
|
226 |
+
"""
|
227 |
+
Adds a custom message to the image with specified font, positioning, and background color.
|
228 |
+
Supports text wrapping and transparent background behind the text.
|
229 |
+
"""
|
230 |
+
# Load font
|
231 |
+
try:
|
232 |
+
font = ImageFont.truetype(font_path, font_size)
|
233 |
+
except IOError:
|
234 |
+
font = ImageFont.load_default()
|
235 |
+
|
236 |
+
# Convert image to RGBA if it's not already
|
237 |
+
if image.mode != "RGBA":
|
238 |
+
image = image.convert("RGBA")
|
239 |
+
|
240 |
+
# Create an overlay for the text
|
241 |
+
overlay = Image.new("RGBA", image.size, (255, 255, 255, 0)) # Fully transparent
|
242 |
+
draw = ImageDraw.Draw(overlay)
|
243 |
+
|
244 |
+
# Wrap the message text
|
245 |
+
message = wrap_text(message, max_chars)
|
246 |
+
|
247 |
+
img_width, img_height = image.size
|
248 |
+
text_lines = message.split("\n")
|
249 |
+
line_height = draw.textbbox((0, 0), "A", font=font)[3] # Calculate height of a line of text
|
250 |
+
total_text_height = line_height * len(text_lines)
|
251 |
+
text_width = max([draw.textbbox((0, 0), line, font=font)[2] for line in text_lines])
|
252 |
+
|
253 |
+
# Horizontal positioning
|
254 |
+
if position_horizontal == "Left":
|
255 |
+
x_pos = 10 # Padding from the left
|
256 |
+
elif position_horizontal == "Center":
|
257 |
+
x_pos = (img_width - text_width) // 2
|
258 |
+
else: # "Right"
|
259 |
+
x_pos = img_width - text_width - 10 # Padding from the right
|
260 |
+
|
261 |
+
# Vertical positioning
|
262 |
+
if position_vertical == "Top":
|
263 |
+
y_pos = 10 # Padding from the top
|
264 |
+
elif position_vertical == "Center":
|
265 |
+
y_pos = (img_height - total_text_height) // 2
|
266 |
+
else: # "Bottom"
|
267 |
+
y_pos = img_height - total_text_height - 10 # Padding from the bottom
|
268 |
+
|
269 |
+
# Draw the semi-transparent background rectangle behind the text
|
270 |
+
padding = 10
|
271 |
+
bg_color_rgba = (*ImageColor.getrgb(bg_color), alpha) # Apply transparency
|
272 |
+
draw.rectangle([x_pos - padding, y_pos - padding, x_pos + text_width + padding, y_pos + total_text_height + padding], fill=bg_color_rgba)
|
273 |
+
|
274 |
+
# Draw the text line by line
|
275 |
+
for i, line in enumerate(text_lines):
|
276 |
+
draw.text((x_pos, y_pos + i * line_height), line, font=font, fill=font_color)
|
277 |
+
|
278 |
+
# Composite the overlay with the original image
|
279 |
+
combined = Image.alpha_composite(image, overlay)
|
280 |
+
|
281 |
+
return combined.convert("RGB") # Convert back to RGB for saving/display
|