Mikiko Bazeley
commited on
Commit
·
158efc9
1
Parent(s):
5938dae
Added different variants of card generation
Browse files
pages/1_Generate_Holiday_Postcard.py
ADDED
@@ -0,0 +1,195 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
|
9 |
+
# Set page configuration
|
10 |
+
st.set_page_config(page_title="FLUX Image Generation Tool", page_icon="🎇")
|
11 |
+
|
12 |
+
# Correct the path to the .env file to reflect its location
|
13 |
+
dotenv_path = os.path.join(os.path.dirname(__file__), '../env/.env')
|
14 |
+
|
15 |
+
# Load environment variables from the .env file
|
16 |
+
load_dotenv(dotenv_path, override=True)
|
17 |
+
|
18 |
+
# Get the Fireworks API key from the .env file
|
19 |
+
fireworks_api_key = os.getenv("FIREWORKS_API_KEY")
|
20 |
+
|
21 |
+
if not fireworks_api_key:
|
22 |
+
st.error("API key not found. Make sure FIREWORKS_API_KEY is set in the .env file.")
|
23 |
+
|
24 |
+
# Define the fonts you want to allow
|
25 |
+
fonts = {
|
26 |
+
"DejaVu Sans Bold": "./fonts/dejavu-sans-bold.ttf",
|
27 |
+
"Covered By You": "./fonts/CoveredByYourGrace-Regular.ttf",
|
28 |
+
"Julee Regular": "./fonts/Julee-Regular.ttf",
|
29 |
+
"Kalam Regular": "./fonts/Kalam-Regular.ttf",
|
30 |
+
"Knewave Regular": "./fonts/Knewave-Regular.ttf",
|
31 |
+
"Sancreek Regular": "./fonts/Sancreek-Regular.ttf",
|
32 |
+
"Vast Shadow Regular": "./fonts/VastShadow-Regular.ttf",
|
33 |
+
}
|
34 |
+
|
35 |
+
# Holiday-themed prompts list
|
36 |
+
holiday_prompts = [
|
37 |
+
"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",
|
38 |
+
"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",
|
39 |
+
"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",
|
40 |
+
"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",
|
41 |
+
"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",
|
42 |
+
"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",
|
43 |
+
"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",
|
44 |
+
"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",
|
45 |
+
"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",
|
46 |
+
"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",
|
47 |
+
"A bountiful Thanksgiving table scene set outdoors, surrounded by autumn leaves, pumpkins, and a golden sunset, capturing the essence of gratitude and togetherness",
|
48 |
+
"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",
|
49 |
+
"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",
|
50 |
+
"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",
|
51 |
+
"A peaceful Easter garden filled with blooming spring flowers, pastel-colored eggs hidden among the grass, and soft sunlight streaming through the trees",
|
52 |
+
"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",
|
53 |
+
"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",
|
54 |
+
"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",
|
55 |
+
"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"
|
56 |
+
]
|
57 |
+
|
58 |
+
|
59 |
+
|
60 |
+
# Function to make requests to the FLUX models
|
61 |
+
def generate_flux_image(model_path, prompt, steps):
|
62 |
+
url = f"https://api.fireworks.ai/inference/v1/workflows/accounts/fireworks/models/{model_path}/text_to_image"
|
63 |
+
headers = {
|
64 |
+
"Authorization": f"Bearer {fireworks_api_key}",
|
65 |
+
"Content-Type": "application/json",
|
66 |
+
"Accept": "image/jpeg"
|
67 |
+
}
|
68 |
+
data = {
|
69 |
+
"prompt": prompt,
|
70 |
+
"aspect_ratio": "16:9",
|
71 |
+
"guidance_scale": 3.5,
|
72 |
+
"num_inference_steps": steps,
|
73 |
+
"seed": 0
|
74 |
+
}
|
75 |
+
|
76 |
+
# Send the request
|
77 |
+
response = requests.post(url, headers=headers, json=data)
|
78 |
+
|
79 |
+
if response.status_code == 200:
|
80 |
+
# Convert the response to an image
|
81 |
+
img_data = response.content
|
82 |
+
img = Image.open(BytesIO(img_data))
|
83 |
+
return img
|
84 |
+
else:
|
85 |
+
raise RuntimeError(f"Error with FLUX model {model_path}: {response.text}")
|
86 |
+
|
87 |
+
# Function to wrap text to a specified number of characters
|
88 |
+
def wrap_text(text, max_chars):
|
89 |
+
"""Wrap text to a specified number of characters per line."""
|
90 |
+
return "\n".join(textwrap.fill(line, width=max_chars) for line in text.split("\n"))
|
91 |
+
|
92 |
+
# Function to add the holiday message to the image
|
93 |
+
def add_custom_message(image, message, font_path, font_size, position_vertical, position_horizontal, max_chars, bg_color, font_color):
|
94 |
+
try:
|
95 |
+
font = ImageFont.truetype(font_path, font_size)
|
96 |
+
except IOError:
|
97 |
+
font = ImageFont.load_default()
|
98 |
+
|
99 |
+
draw = ImageDraw.Draw(image)
|
100 |
+
|
101 |
+
# Wrap the text based on the max_chars input
|
102 |
+
message = wrap_text(message, max_chars)
|
103 |
+
|
104 |
+
img_width, img_height = image.size
|
105 |
+
|
106 |
+
# Calculate the text bounding box dynamically
|
107 |
+
text_lines = message.split("\n")
|
108 |
+
line_height = draw.textbbox((0, 0), "A", font=font)[3] # Get height of one line of text (bbox returns top, left, bottom, right)
|
109 |
+
total_text_height = line_height * len(text_lines)
|
110 |
+
text_width = max([draw.textbbox((0, 0), line, font=font)[2] for line in text_lines])
|
111 |
+
|
112 |
+
# Horizontal positioning
|
113 |
+
if position_horizontal == "Left":
|
114 |
+
x_pos = 10 # Padding from the left
|
115 |
+
elif position_horizontal == "Center":
|
116 |
+
x_pos = (img_width - text_width) // 2
|
117 |
+
else: # "Right"
|
118 |
+
x_pos = img_width - text_width - 10 # Padding from the right
|
119 |
+
|
120 |
+
# Vertical positioning
|
121 |
+
if position_vertical == "Top":
|
122 |
+
y_pos = 10 # Padding from the top
|
123 |
+
elif position_vertical == "Center":
|
124 |
+
y_pos = (img_height - total_text_height) // 2
|
125 |
+
else: # "Bottom"
|
126 |
+
y_pos = img_height - total_text_height - 10 # Padding from the bottom
|
127 |
+
|
128 |
+
# Define the padding for the background box behind the text
|
129 |
+
padding = 10
|
130 |
+
|
131 |
+
# Draw the background rectangle (the text box) with user-specified background color
|
132 |
+
draw.rectangle([x_pos - padding, y_pos - padding, x_pos + text_width + padding, y_pos + total_text_height + padding], fill=bg_color)
|
133 |
+
|
134 |
+
# Draw the text line by line with the user-specified font color
|
135 |
+
for i, line in enumerate(text_lines):
|
136 |
+
draw.text((x_pos, y_pos + i * line_height), line, font=font, fill=font_color)
|
137 |
+
|
138 |
+
return image
|
139 |
+
|
140 |
+
# Streamlit UI
|
141 |
+
st.title("FLUX Image Generation")
|
142 |
+
st.write("Generate images using the FLUX models and customize them with a holiday message.")
|
143 |
+
|
144 |
+
# Dropdown to select a holiday-themed prompt or enter a custom prompt
|
145 |
+
selected_prompt = st.selectbox("Choose a holiday-themed prompt or enter your own", options=["Custom"] + holiday_prompts)
|
146 |
+
custom_prompt = st.text_input("Enter your custom prompt") if selected_prompt == "Custom" else ""
|
147 |
+
prompt = custom_prompt if selected_prompt == "Custom" else selected_prompt
|
148 |
+
|
149 |
+
# Dropdown to select the model
|
150 |
+
model_choice = st.selectbox("Select the model:", ["flux-1-dev", "flux-1-schnell"])
|
151 |
+
|
152 |
+
# Additional inputs for customizing the message
|
153 |
+
message = st.text_input("Enter a holiday message to add:")
|
154 |
+
font_choice = st.selectbox("Select a font:", list(fonts.keys()))
|
155 |
+
font_size = st.slider("Select font size:", 10, 100, 40)
|
156 |
+
max_chars = st.slider("Max characters per line:", 10, 100, 40) # Slider to select character wrap limit
|
157 |
+
|
158 |
+
# Background color and font color pickers
|
159 |
+
bg_color = st.color_picker("Pick a background color for the text box:", "#FFFFFF")
|
160 |
+
font_color = st.color_picker("Pick a font color:", "#000000")
|
161 |
+
|
162 |
+
# Position options for the message (vertical and horizontal)
|
163 |
+
position_vertical = st.radio("Select message vertical position:", ["Top", "Center", "Bottom"])
|
164 |
+
position_horizontal = st.radio("Select message horizontal position:", ["Left", "Center", "Right"])
|
165 |
+
|
166 |
+
# Button to generate images
|
167 |
+
if st.button("Generate Image"):
|
168 |
+
if not prompt.strip():
|
169 |
+
st.error("Please provide a prompt.")
|
170 |
+
else:
|
171 |
+
try:
|
172 |
+
with st.spinner("Generating image..."):
|
173 |
+
# Determine steps based on model
|
174 |
+
steps = 30 if model_choice == "flux-1-dev" else 4
|
175 |
+
|
176 |
+
# Generate image
|
177 |
+
generated_image = generate_flux_image(model_choice, prompt, steps)
|
178 |
+
|
179 |
+
# Get the selected font path
|
180 |
+
font_path = fonts[font_choice]
|
181 |
+
|
182 |
+
# Add the holiday message to the generated image
|
183 |
+
image_with_message = add_custom_message(
|
184 |
+
generated_image.copy(), message, font_path, font_size,
|
185 |
+
position_vertical, position_horizontal, max_chars, bg_color, font_color
|
186 |
+
)
|
187 |
+
|
188 |
+
# Display the image with the message
|
189 |
+
st.image(image_with_message, caption=f"Generated using {model_choice} with custom message", use_column_width=True)
|
190 |
+
|
191 |
+
# Preview the placement
|
192 |
+
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})")
|
193 |
+
|
194 |
+
except Exception as e:
|
195 |
+
st.error(f"An error occurred: {e}")
|
pages/{1_Part_A_-_Experimentation_Station.py → 2_Generate_Holiday_Borders.py}
RENAMED
@@ -368,15 +368,4 @@ if any(st.session_state.generated_cards):
|
|
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)
|
372 |
-
|
373 |
-
# """ zip_buffer.seek(0)
|
374 |
-
# st.divider()
|
375 |
-
# st.subheader("Save images & metadata")
|
376 |
-
# st.download_button(
|
377 |
-
# label="Download all previously generated images and metadata as ZIP",
|
378 |
-
# data=zip_buffer,
|
379 |
-
# file_name="holiday_cards.zip",
|
380 |
-
# mime="application/zip"
|
381 |
-
# )
|
382 |
-
# """
|
|
|
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_Part_B_-_Card_Generator.py → 3_Customize_Holiday_Borders.py}
RENAMED
@@ -220,7 +220,7 @@ if st.session_state.uploaded_file is not None:
|
|
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
|
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!")
|
|
|
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!")
|
pages/4_Morph_Holiday_Card.py
ADDED
@@ -0,0 +1,268 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
|