Mikiko Bazeley commited on
Commit
57eccf2
·
1 Parent(s): c6c35c1

Refactored and removed controlnet

Browse files
.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
- \n 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 you can do:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- st.subheader("Experiment with Flux to figure out the perfect look for your holiday card")
51
- st.markdown(""" ![For science!](https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExbGwwdGh4czc3bnU3czNzaWRhaXl4YXYzdm53Nmw1OXhpd2E4emFuYSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/RdJIM4Uesg37eN1ZNL/giphy.gif)""")
 
 
 
 
 
 
 
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
- st.divider()
65
- st.subheader("Customize your holiday card with a special message")
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
- import os
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
- # Correct the path to the .env file to reflect its location
15
- dotenv_path = os.path.join(os.path.dirname(__file__), '../env/.env')
16
-
17
- # Load environment variables from the .env file
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
- # Convert image to RGBA if it's not already
128
- if image.mode != "RGBA":
129
- image = image.convert("RGBA")
130
 
131
- # Create an overlay image with the same size and transparency (RGBA mode)
132
- overlay = Image.new("RGBA", image.size, (255, 255, 255, 0)) # Fully transparent
133
 
134
- draw = ImageDraw.Draw(overlay)
 
 
 
 
135
 
136
- # Wrap the text based on the max_chars input
137
- message = wrap_text(message, max_chars)
138
 
139
- img_width, img_height = image.size
 
140
 
141
- # Calculate the text bounding box dynamically
142
- text_lines = message.split("\n")
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
- # Horizontal positioning
148
- if position_horizontal == "Left":
149
- x_pos = 10 # Padding from the left
150
- elif position_horizontal == "Center":
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
- # Vertical positioning
156
- if position_vertical == "Top":
157
- y_pos = 10 # Padding from the top
158
- elif position_vertical == "Center":
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
- # Define the padding for the background box behind the text
164
- padding = 10
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
- selected_prompt = st.selectbox("Choose a holiday-themed prompt or enter your own", options=["Custom"] + holiday_prompts)
 
 
 
 
 
 
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
- model_choice = st.selectbox("Select the model:", ["flux-1-schnell", "flux-1-dev"])
 
 
 
 
 
 
195
 
196
  # Dropdown to select an example message or write a custom one
197
- selected_message = st.selectbox("Choose an example message or write your own:", options=["Custom"] + example_messages)
 
 
 
 
 
 
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:", 10, 100, 40)
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
- # **Add a transparency slider for controlling the alpha channel**
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 st.button("Generate Image"):
 
 
 
 
 
 
 
 
 
 
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 dotenv import load_dotenv
 
9
  import zipfile
 
10
 
11
- # Allow truncated images to be loaded
12
- ImageFile.LOAD_TRUNCATED_IMAGES = True
 
 
 
13
 
14
- # Load environment variables
15
- dotenv_path = os.path.join(os.path.dirname(__file__), '../env/.env')
16
- load_dotenv(dotenv_path, override=True)
17
- api_key = os.getenv("FIREWORKS_API_KEY")
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
- st.markdown("""
141
- Welcome back! You've already set your favorite parameters, and now it's time to finalize your custom holiday card.
 
142
 
143
- **Steps**:
144
- 1. **Choose a Border**: Select or confirm a holiday-themed border to frame your image—choose from snowflakes, ornaments, cozy fireplaces, and more!
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
- **Features**:
149
- - Confirm your AI-generated border with your preferred parameters
150
- - Personalize the message with a variety of fonts and sizes
151
- - Save your card with metadata for future use
152
 
153
- 🎁 Get your perfect holiday card ready to share! 🌟
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
- # File uploader - if a file is uploaded, save it in session_state
166
- st.subheader("🖼️ Step 1: Upload Your Picture!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  st.markdown("""
168
- - Click on the "Upload Image" button to select the image you want to feature on your holiday card. Make sure it's a **JPG** or **PNG** file.
169
- - Once uploaded, your image will appear on the screen—easy peasy!
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 # Save to session state
174
- st.divider()
175
 
176
- # Load the image from session state if it exists
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
- img_width, img_height = original_image.size
184
- st.divider()
185
-
186
- st.subheader("✂️ Step 2: Crop It Like It's Hot!")
187
  st.markdown("""
188
- - Adjust the **crop** sliders to select the perfect area of your image. This cropped section will be the centerpiece of your festive card. 🎨
189
- - A preview will show you exactly how your crop looks before moving to the next step!
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
- help="Move the slider to adjust the crop's left-right position.")
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
- help="Move the slider to adjust the crop's up-down position.")
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
- # Holiday card prompt and parameters
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
- - 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!** ✨
229
- - 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**. """)
230
- selected_prompt = st.selectbox("Choose a holiday-themed prompt or enter your own", options=["Custom"] + holiday_prompts)
 
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
- with st.expander("Advanced Parameters"):
235
- control_mode = st.slider("Control Mode", min_value=0, max_value=2, value=0)
236
- controlnet_conditioning_scale = st.slider("ControlNet Conditioning Scale", min_value=0.0, max_value=1.0, value=0.5, step=0.1)
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("Processing..."):
246
- generated_image, processed_image, _ = call_control_net_api(
247
- st.session_state.uploaded_file, prompt, control_mode=control_mode,
248
- guidance_scale=guidance_scale, num_inference_steps=num_inference_steps,
249
- seed=seed, controlnet_conditioning_scale=controlnet_conditioning_scale
 
 
 
 
 
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
- # Get the size of the cropped image
263
- cropped_width, cropped_height = cropped_original.size
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 the image locally
276
- final_image.save(f"{GENERATED_IMAGES_DIR}/holiday_card.png")
 
 
 
 
 
277
 
278
- # Display the final holiday card
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
- # Add a text box to input a custom message (limit to 280 characters like a tweet)
284
- st.divider()
285
- if 'generated_image' in st.session_state:
286
- st.subheader("✏️ Step 4: Add Your Custom Holiday Message!")
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
- # Text input for custom message
292
- custom_message = st.text_input("Add a custom holiday message to the card (max 280 characters)", max_chars=280)
 
 
 
 
 
293
 
294
- # Font selection dropdown
 
 
295
  selected_font = st.selectbox("Choose a font for your message", options=list(fonts.keys()))
296
-
297
- # Slider to allow the user to adjust the font size
298
- font_size = st.slider("Adjust font size", min_value=10, max_value=500, value=100, step=10)
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
- # Get the dimensions of the cropped image and final image
307
- cropped_bottom_y = y_pos + crop_height # y_pos is the top Y position of the cropped area
308
-
309
- # Add the custom message to the image
310
- if custom_message:
311
- final_image_with_message = add_message_to_image(final_image_with_message, cropped_bottom_y, custom_message, font_path, font_size)
 
 
 
 
 
 
312
 
313
- # Display the final holiday card with the message
314
- st.image(final_image_with_message, caption="Holiday Card with Custom Message", use_column_width=True)
315
 
316
- # Save image for download
317
  img_byte_arr = BytesIO()
318
  final_image_with_message.save(img_byte_arr, format="PNG")
319
  img_byte_arr.seek(0)
320
 
321
- # Download button for the final image
322
- st.divider()
323
- st.subheader("📥 Step 5: Download & Share Your Masterpiece!")
324
- st.markdown("""
325
- - Once you're happy with your card, simply hit the download button to save your card and message as a PNG image.
326
- - You can also view and download any **previously generated holiday cards** from this session!""")
 
 
 
 
 
 
 
327
  st.download_button(
328
- label="Download Holiday Card with Message",
329
- data=img_byte_arr,
330
- file_name="holiday_card_with_message.png",
331
- mime="image/png"
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
- saved_image_path = f"{GENERATED_IMAGES_DIR}/holiday_card_with_message.png"
346
- if os.path.exists(saved_image_path):
347
- saved_image = Image.open(saved_image_path)
348
- st.image(saved_image, caption="Previously Generated Holiday Card", use_column_width=True)
 
 
 
 
 
 
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__), '../env/.env')
 
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
- # model_path = "flux-1-dev" # Uncomment if you want to switch to the dev model
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