Mikiko Bazeley
commited on
Commit
·
7c1b343
1
Parent(s):
be47fca
Wrapping up documentation, copy
Browse files- .gitignore +1 -1
- README.md +49 -79
- app.py +68 -3
- fonts/CoveredByYourGrace-Regular.ttf +0 -0
- fonts/Julee-Regular.ttf +0 -0
- fonts/Kalam-Regular.ttf +0 -0
- fonts/Knewave-Regular.ttf +0 -0
- fonts/Sancreek-Regular.ttf +0 -0
- fonts/VastShadow-Regular.ttf +0 -0
- fonts/dejavu-sans-bold.ttf +0 -0
- img/card_with_message_1.png +0 -0
- img/card_with_message_2.png +0 -0
- img/parameters_1.png +0 -0
- img/parameters_2.png +0 -0
- img/parameters_3.png +0 -0
- img/parameters_4.png +0 -0
- pages/1_Autocenter_HolidayCard.py +0 -178
- pages/{2_Multiple_HolidayCard.py → 1_Part_A_-_Experimentation_Station.py} +206 -22
- pages/2_Customize_HolidayCard.py +0 -322
- pages/2_Part_B_-_Card_Generator.py +338 -0
- pages/3_Pure_FLUX_ImageGeneration.py +0 -77
.gitignore
CHANGED
@@ -261,7 +261,7 @@ venv.bak/
|
|
261 |
|
262 |
# Assets and images generated
|
263 |
run_*/
|
264 |
-
|
265 |
|
266 |
# Spyder project settings
|
267 |
.spyderproject
|
|
|
261 |
|
262 |
# Assets and images generated
|
263 |
run_*/
|
264 |
+
pages_draft/*
|
265 |
|
266 |
# Spyder project settings
|
267 |
.spyderproject
|
README.md
CHANGED
@@ -11,128 +11,98 @@ license: mit
|
|
11 |
short_description: Generate an ugly hold
|
12 |
---
|
13 |
|
14 |
-
|
15 |
|
16 |
-
|
17 |
-
The **Fireworks Model Comparison App** is an interactive tool built using **Streamlit** that allows users to compare various Large Language Models (LLMs) hosted on **Fireworks AI**. Users can adjust key model parameters, provide custom prompts, and generate model outputs to compare their behavior and responses. Additionally, an LLM-as-a-Judge feature is available to evaluate the generated outputs and provide feedback on their quality.
|
18 |
|
|
|
19 |
|
20 |
-
|
21 |
-
- **Compare Models**: Select different models from the Fireworks platform and compare their outputs based on a shared prompt.
|
22 |
-
- **Modify Parameters**: Fine-tune parameters such as **Max Tokens**, **Temperature**, **Top-p**, and **Top-k** to observe how they influence model behavior.
|
23 |
-
- **Evaluate Using LLM-as-a-Judge**: After generating responses, use a separate model to act as a judge and evaluate the outputs from the selected models.
|
24 |
-
|
25 |
|
26 |
-
|
27 |
-
|
28 |
|
|
|
|
|
|
|
|
|
|
|
29 |
|
30 |
-
###
|
31 |
-
|
32 |
-
- **LLM Comparison**: Select up to three different models, run a query with the same prompt, and view side-by-side responses.
|
33 |
-
- **Parameter Exploration**: Explore and modify different parameters such as Max Tokens, Temperature, Top-p, and more to see how they affect the model's response.
|
34 |
-
- **LLM-as-a-Judge**: Let another LLM compare the generated responses from the models and provide a comparison.
|
35 |
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
-
|
40 |
-
- View the responses side-by-side for easy comparison.
|
41 |
-
- A selected LLM acts as a judge to evaluate the generated responses.
|
42 |
|
|
|
|
|
|
|
43 |
|
44 |
-
|
45 |
-
![Home Page Screenshot](img/page_1_a.png)
|
46 |
-
![Home Page Screenshot](img/page_1_b.png)
|
47 |
-
![Home Page Screenshot](img/page_1_c.png)
|
48 |
|
49 |
-
|
50 |
-
2. **Parameter Exploration**:
|
51 |
-
- Modify various parameters for the LLMs (e.g., Max Tokens, Temperature, Top-p) and observe how they affect the outputs.
|
52 |
-
- Compare three different outputs generated with varying parameter configurations.
|
53 |
-
- Use LLM-as-a-Judge to provide a final evaluation of the outputs.
|
54 |
-
|
55 |
-
![Home Page Screenshot](img/page_2_empty.png)
|
56 |
-
![Home Page Screenshot](img/page_2_a.png)
|
57 |
-
![Home Page Screenshot](img/page_2_b.png)
|
58 |
-
![Home Page Screenshot](img/page_2_c.png)
|
59 |
-
|
60 |
-
### Setup and Installation
|
61 |
-
|
62 |
-
#### Prerequisites
|
63 |
- **Python 3.x** installed on your machine.
|
64 |
- A **Fireworks AI** API key, which you can obtain by signing up at [Fireworks AI](https://fireworks.ai).
|
65 |
- Install **Streamlit** and the **Fireworks Python Client**.
|
66 |
|
67 |
-
#### Step-by-Step Setup
|
68 |
-
##### 1. Clone the Repository:
|
69 |
-
First, clone the repository from GitHub:
|
70 |
|
|
|
71 |
```bash
|
72 |
git clone https://github.com/fw-ai/examples.git
|
73 |
```
|
74 |
|
75 |
-
|
76 |
-
After cloning the repository, navigate to the `project_llm-as-a-judge-streamlit-dashboard` sub-directory:
|
77 |
-
|
78 |
```bash
|
79 |
-
cd learn/inference/
|
80 |
```
|
81 |
|
82 |
-
|
83 |
-
Create and activate a Python virtual environment:
|
84 |
-
|
85 |
```bash
|
86 |
python3 -m venv venv
|
87 |
source venv/bin/activate # On macOS/Linux
|
88 |
.\venv\Scripts\activate # On Windows
|
89 |
```
|
90 |
|
91 |
-
|
92 |
-
Install the necessary Python dependencies using `pip3`:
|
93 |
-
|
94 |
```bash
|
95 |
-
|
96 |
```
|
97 |
|
98 |
-
|
99 |
-
Copy the `.env.template` file and rename it to `.env` in the
|
100 |
-
|
101 |
```bash
|
102 |
-
mkdir env/
|
103 |
cp .env.template env/.env
|
104 |
```
|
105 |
-
|
106 |
-
Open the `.env` file and add your **FIREWORKS_API_KEY**:
|
107 |
-
|
108 |
```bash
|
109 |
FIREWORKS_API_KEY=<your_fireworks_api_key>
|
110 |
```
|
111 |
|
112 |
-
|
113 |
-
Finally, run the Streamlit app:
|
114 |
-
|
115 |
```bash
|
116 |
-
streamlit run
|
117 |
```
|
118 |
|
|
|
|
|
|
|
119 |
|
120 |
-
|
121 |
-
|
122 |
-
|
|
|
|
|
123 |
|
124 |
-
###
|
125 |
-
|
126 |
-
- **Prompt 1**: "Describe the future of AI in 500 words."
|
127 |
-
- **Prompt 2**: "Write a short story about a time traveler who visits ancient Rome."
|
128 |
-
- **Prompt 3**: "Explain quantum computing in simple terms."
|
129 |
-
- **Prompt 4**: "Generate a recipe for a healthy vegan dinner."
|
130 |
|
131 |
-
###
|
132 |
-
|
133 |
|
134 |
-
###
|
135 |
-
|
136 |
|
137 |
-
|
138 |
-
This project is licensed under the MIT License.
|
|
|
11 |
short_description: Generate an ugly hold
|
12 |
---
|
13 |
|
14 |
+
🎉 **Ugly Holiday Card Generator** 🎉
|
15 |
|
16 |
+
👋 **Welcome to the Ugly Holiday Card Generator!** 🎄❄️
|
|
|
17 |
|
18 |
+
This fun and festive app allows you to create your own ugly holiday cards using AI-generated borders, custom messages, and creative controls to make your card as unique (and ugly!) as possible. Powered by Flux models on Fireworks AI, this app helps you get creative with your holiday greetings. 🎅🎁✨
|
19 |
|
20 |
+
## 🎯 **Project: Ugly Holiday Card Generator**
|
|
|
|
|
|
|
|
|
21 |
|
22 |
+
### 🛠️ **Overview**
|
23 |
+
The **Ugly Holiday Card Generator** is an interactive tool built with **Streamlit** that allows users to upload images, adjust parameters, and generate customized holiday cards with AI-generated borders. Whether you want to add snowflakes, Christmas lights, or even fireworks, this app lets you craft the perfect holiday card to share with your friends and family! 🌟
|
24 |
|
25 |
+
### 🎨 **Features**
|
26 |
+
- **Custom Borders**: Choose from a variety of holiday-themed prompts like festive snowflakes, cozy fireplaces, and colorful fireworks! Or create your own custom border prompt! 🖼️🎆
|
27 |
+
- **Crop It**: Adjust your image’s crop to focus on the best part of your photo. ✂️
|
28 |
+
- **Personalized Message**: Add your own holiday message in different fonts and sizes to give it that final personal touch. ✏️💌
|
29 |
+
- **Save & Download**: Easily download your masterpiece with just a click and share it with the world! 🌍🎁
|
30 |
|
31 |
+
### 📝 **App Structure**
|
32 |
+
The app is divided into two parts:
|
|
|
|
|
|
|
33 |
|
34 |
+
1. **Prompt & Parameter Ideation**:
|
35 |
+
- Select or enter custom prompts for your holiday card's border.
|
36 |
+
- Adjust advanced parameters like **guidance scale** and **inference steps** to fine-tune your card.
|
37 |
+
- Crop your image and preview your card before moving to the final stage. 🎄📸
|
|
|
|
|
38 |
|
39 |
+
2. **Personalization & Message Customization**:
|
40 |
+
- Add a custom holiday message with a variety of fonts and sizes.
|
41 |
+
- View the final card with your personalized message and download it in PNG format. 🖼️💌
|
42 |
|
43 |
+
### 💻 **Setup and Installation**
|
|
|
|
|
|
|
44 |
|
45 |
+
#### 🚀 **Prerequisites**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
- **Python 3.x** installed on your machine.
|
47 |
- A **Fireworks AI** API key, which you can obtain by signing up at [Fireworks AI](https://fireworks.ai).
|
48 |
- Install **Streamlit** and the **Fireworks Python Client**.
|
49 |
|
50 |
+
#### �� **Step-by-Step Setup**
|
|
|
|
|
51 |
|
52 |
+
1. **Clone the Repository**:
|
53 |
```bash
|
54 |
git clone https://github.com/fw-ai/examples.git
|
55 |
```
|
56 |
|
57 |
+
2. **Navigate to the Specific Project Sub-directory**:
|
|
|
|
|
58 |
```bash
|
59 |
+
cd learn/inference/ugly-holiday-card-generator
|
60 |
```
|
61 |
|
62 |
+
3. **Set up a Virtual Environment** (Optional but Recommended):
|
|
|
|
|
63 |
```bash
|
64 |
python3 -m venv venv
|
65 |
source venv/bin/activate # On macOS/Linux
|
66 |
.\venv\Scripts\activate # On Windows
|
67 |
```
|
68 |
|
69 |
+
4. **Install Required Dependencies**:
|
|
|
|
|
70 |
```bash
|
71 |
+
pip install -r requirements.txt
|
72 |
```
|
73 |
|
74 |
+
5. **Configure the `.env` File**:
|
75 |
+
Copy the `.env.template` file and rename it to `.env` in the project directory:
|
|
|
76 |
```bash
|
|
|
77 |
cp .env.template env/.env
|
78 |
```
|
79 |
+
Add your **FIREWORKS_API_KEY**:
|
|
|
|
|
80 |
```bash
|
81 |
FIREWORKS_API_KEY=<your_fireworks_api_key>
|
82 |
```
|
83 |
|
84 |
+
6. **Run the Streamlit App**:
|
|
|
|
|
85 |
```bash
|
86 |
+
streamlit run app.py
|
87 |
```
|
88 |
|
89 |
+
7. **Explore the App**:
|
90 |
+
- Open the app in your browser via the URL provided by Streamlit (typically `http://localhost:8501`).
|
91 |
+
- Navigate between the prompt ideation and message customization stages. ✨🎨
|
92 |
|
93 |
+
### 📝 **Example Prompts**
|
94 |
+
Here are some example holiday prompts to try in the app:
|
95 |
+
- "A border of festive snowflakes and winter patterns"
|
96 |
+
- "A border of cozy Christmas ornaments and lights"
|
97 |
+
- "A border of New Year's Eve fireworks with stars and confetti"
|
98 |
|
99 |
+
### 🔗 **Fireworks API Documentation**
|
100 |
+
To learn more about querying models and interacting with the Fireworks API, check out the [Fireworks API Documentation](https://docs.fireworks.ai/api-reference).
|
|
|
|
|
|
|
|
|
101 |
|
102 |
+
### 🤝 **Contributing**
|
103 |
+
Contributions are welcome! Feel free to fork the repository, make changes, and submit a pull request. 🎨🎄
|
104 |
|
105 |
+
### 📝 **License**
|
106 |
+
This project is licensed under the MIT License.
|
107 |
|
108 |
+
🎁 **Happy Holidays and Enjoy Creating Your Cards!** 🎁
|
|
app.py
CHANGED
@@ -1,6 +1,71 @@
|
|
1 |
import streamlit as st
|
2 |
from PIL import Image
|
3 |
|
4 |
-
|
5 |
-
|
6 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import streamlit as st
|
2 |
from PIL import Image
|
3 |
|
4 |
+
|
5 |
+
# Load images
|
6 |
+
logo_image = Image.open("img/fireworksai_logo.png")
|
7 |
+
parameters_1_image = Image.open("img/parameters_1.png")
|
8 |
+
parameters_2_image = Image.open("img/parameters_2.png")
|
9 |
+
parameters_3_image = Image.open("img/parameters_3.png")
|
10 |
+
parameters_4_image = Image.open("img/parameters_4.png")
|
11 |
+
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 |
+
|
19 |
+
# Title of the app
|
20 |
+
st.title("🎨 Flux Holiday Magic: Custom Card Creator")
|
21 |
+
|
22 |
+
|
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
|
31 |
+
st.markdown("""
|
32 |
+
- **Upload Your Image**: Choose any photo as the base for your holiday card.
|
33 |
+
- **Crop & Preview**: Fine-tune the area of your image to fit perfectly within the card.
|
34 |
+
- **Holiday Borders**: Choose from festive prompts like snowflakes, ornaments, and cozy fireplace scenes to frame your photo.
|
35 |
+
- **Add Custom Messages**: Personalize your card with a message, choosing from a variety of fonts and adjustable text sizes.
|
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 |
+
""")
|
43 |
+
|
44 |
+
# Call to action
|
45 |
+
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:
|
55 |
+
st.image(parameters_1_image)
|
56 |
+
st.image(parameters_3_image)
|
57 |
+
|
58 |
+
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)
|
fonts/CoveredByYourGrace-Regular.ttf
ADDED
Binary file (50.1 kB). View file
|
|
fonts/Julee-Regular.ttf
ADDED
Binary file (65.4 kB). View file
|
|
fonts/Kalam-Regular.ttf
ADDED
Binary file (427 kB). View file
|
|
fonts/Knewave-Regular.ttf
ADDED
Binary file (31.3 kB). View file
|
|
fonts/Sancreek-Regular.ttf
ADDED
Binary file (47.6 kB). View file
|
|
fonts/VastShadow-Regular.ttf
ADDED
Binary file (59.5 kB). View file
|
|
fonts/dejavu-sans-bold.ttf
ADDED
Binary file (706 kB). View file
|
|
img/card_with_message_1.png
ADDED
![]() |
img/card_with_message_2.png
ADDED
![]() |
img/parameters_1.png
ADDED
![]() |
img/parameters_2.png
ADDED
![]() |
img/parameters_3.png
ADDED
![]() |
img/parameters_4.png
ADDED
![]() |
pages/1_Autocenter_HolidayCard.py
DELETED
@@ -1,178 +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 |
-
|
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 |
-
VALID_ASPECT_RATIOS = {
|
20 |
-
(1, 1): "1:1", (21, 9): "21:9", (16, 9): "16:9", (3, 2): "3:2", (5, 4): "5:4",
|
21 |
-
(4, 5): "4:5", (2, 3): "2:3", (9, 16): "9:16", (9, 21): "9:21",
|
22 |
-
}
|
23 |
-
|
24 |
-
def get_closest_aspect_ratio(width, height):
|
25 |
-
aspect_ratio = width / height
|
26 |
-
closest_ratio = min(VALID_ASPECT_RATIOS.keys(), key=lambda x: abs((x[0] / x[1]) - aspect_ratio))
|
27 |
-
return VALID_ASPECT_RATIOS[closest_ratio]
|
28 |
-
|
29 |
-
def process_image(uploaded_image):
|
30 |
-
image = np.array(Image.open(uploaded_image).convert('L'))
|
31 |
-
edges = cv2.Canny(image, 100, 200)
|
32 |
-
edges_rgb = cv2.cvtColor(edges, cv2.COLOR_GRAY2RGB)
|
33 |
-
pil_image = Image.fromarray(edges_rgb)
|
34 |
-
byte_arr = BytesIO()
|
35 |
-
pil_image.save(byte_arr, format='JPEG')
|
36 |
-
byte_arr.seek(0)
|
37 |
-
return byte_arr, pil_image
|
38 |
-
|
39 |
-
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):
|
40 |
-
control_image, processed_image = process_image(uploaded_image)
|
41 |
-
files = {'control_image': ('control_image.jpg', control_image, 'image/jpeg')}
|
42 |
-
original_image = Image.open(uploaded_image)
|
43 |
-
width, height = original_image.size
|
44 |
-
aspect_ratio = get_closest_aspect_ratio(width, height)
|
45 |
-
data = {
|
46 |
-
'prompt': prompt,
|
47 |
-
'control_mode': control_mode,
|
48 |
-
'aspect_ratio': aspect_ratio,
|
49 |
-
'guidance_scale': guidance_scale,
|
50 |
-
'num_inference_steps': num_inference_steps,
|
51 |
-
'seed': seed,
|
52 |
-
'controlnet_conditioning_scale': controlnet_conditioning_scale
|
53 |
-
}
|
54 |
-
headers = {
|
55 |
-
'accept': 'image/jpeg',
|
56 |
-
'authorization': f'Bearer {api_key}',
|
57 |
-
}
|
58 |
-
response = requests.post('https://api.fireworks.ai/inference/v1/workflows/accounts/fireworks/models/flux-1-dev-controlnet-union/control_net',
|
59 |
-
files=files, data=data, headers=headers)
|
60 |
-
if response.status_code == 200:
|
61 |
-
return Image.open(BytesIO(response.content)), processed_image, original_image
|
62 |
-
else:
|
63 |
-
st.error(f"Request failed with status code: {response.status_code}, Response: {response.text}")
|
64 |
-
return None, None, None
|
65 |
-
|
66 |
-
def draw_crop_preview(image, x, y, width, height):
|
67 |
-
draw = ImageDraw.Draw(image)
|
68 |
-
draw.rectangle([x, y, x + width, y + height], outline="red", width=2)
|
69 |
-
return image
|
70 |
-
|
71 |
-
st.title("Holiday Card Generator with ControlNet")
|
72 |
-
|
73 |
-
uploaded_file = st.file_uploader("Upload an image", type=["png", "jpg", "jpeg"])
|
74 |
-
|
75 |
-
if uploaded_file is not None:
|
76 |
-
original_image = Image.open(uploaded_file)
|
77 |
-
st.image(original_image, caption="Uploaded Image", use_column_width=True)
|
78 |
-
|
79 |
-
img_width, img_height = original_image.size
|
80 |
-
|
81 |
-
col1, col2 = st.columns(2)
|
82 |
-
with col1:
|
83 |
-
x_pos = st.slider("X position", 0, img_width, img_width // 4)
|
84 |
-
crop_width = st.slider("Width", 10, img_width - x_pos, min(img_width // 2, img_width - x_pos))
|
85 |
-
with col2:
|
86 |
-
y_pos = st.slider("Y position", 0, img_height, img_height // 4)
|
87 |
-
crop_height = st.slider("Height", 10, img_height - y_pos, min(img_height // 2, img_height - y_pos))
|
88 |
-
|
89 |
-
preview_image = draw_crop_preview(original_image.copy(), x_pos, y_pos, crop_width, crop_height)
|
90 |
-
st.image(preview_image, caption="Crop Preview", use_column_width=True)
|
91 |
-
|
92 |
-
st.subheader("Set Parameters for Each Holiday Card")
|
93 |
-
|
94 |
-
# Define the list of suggested holiday prompts
|
95 |
-
holiday_prompts = [
|
96 |
-
"A border of Festive snowflakes and winter patterns for a holiday card border",
|
97 |
-
"A border of Joyful Christmas ornaments and lights decorating the edges",
|
98 |
-
"A border of Warm and cozy fireplace scene with stockings and garlands",
|
99 |
-
"A border of Colorful Hanukkah menorahs and dreidels along the border",
|
100 |
-
"A border of New Year's Eve fireworks with stars and confetti framing the image"
|
101 |
-
]
|
102 |
-
|
103 |
-
# Define input fields for each holiday card's parameters
|
104 |
-
card_params = []
|
105 |
-
for i in range(4):
|
106 |
-
st.write(f"### Holiday Card {i + 1}")
|
107 |
-
|
108 |
-
# Dropdown to choose a suggested holiday prompt or enter custom prompt
|
109 |
-
selected_prompt = st.selectbox(f"Choose a holiday-themed prompt for Holiday Card {i + 1}", options=["Custom"] + holiday_prompts)
|
110 |
-
custom_prompt = st.text_input(f"Enter custom prompt for Holiday Card {i + 1}", value=f"Custom Prompt {i + 1}") if selected_prompt == "Custom" else selected_prompt
|
111 |
-
|
112 |
-
# Parameter sliders for each holiday card
|
113 |
-
guidance_scale = st.slider(f"Guidance Scale for Holiday Card {i + 1}", min_value=0.0, max_value=20.0, value=3.5, step=0.1)
|
114 |
-
num_inference_steps = st.slider(f"Number of Inference Steps for Holiday Card {i + 1}", min_value=1, max_value=100, value=30, step=1)
|
115 |
-
seed = st.slider(f"Random Seed for Holiday Card {i + 1}", min_value=0, max_value=1000, value=i * 100)
|
116 |
-
controlnet_conditioning_scale = st.slider(f"ControlNet Conditioning Scale for Holiday Card {i + 1}", min_value=0.0, max_value=1.0, value=0.5, step=0.1)
|
117 |
-
control_mode = st.slider(f"Control Mode for Holiday Card {i + 1}", min_value=0, max_value=2, value=0, help="0: None, 1: Partial, 2: Full")
|
118 |
-
|
119 |
-
# Save the parameters for each holiday card
|
120 |
-
card_params.append({
|
121 |
-
"prompt": custom_prompt,
|
122 |
-
"guidance_scale": guidance_scale,
|
123 |
-
"num_inference_steps": num_inference_steps,
|
124 |
-
"seed": seed,
|
125 |
-
"controlnet_conditioning_scale": controlnet_conditioning_scale,
|
126 |
-
"control_mode": control_mode
|
127 |
-
})
|
128 |
-
|
129 |
-
# Generate the holiday cards
|
130 |
-
if st.button("Generate Holiday Cards"):
|
131 |
-
with st.spinner("Processing..."):
|
132 |
-
# Create a column layout for displaying cards side by side
|
133 |
-
col1, col2 = st.columns(2)
|
134 |
-
col3, col4 = st.columns(2)
|
135 |
-
columns = [col1, col2, col3, col4] # To display images in a 2x2 grid
|
136 |
-
|
137 |
-
# Loop through each card's parameters and generate the holiday card
|
138 |
-
for i, params in enumerate(card_params):
|
139 |
-
prompt = params['prompt']
|
140 |
-
guidance_scale = params['guidance_scale']
|
141 |
-
num_inference_steps = params['num_inference_steps']
|
142 |
-
seed = params['seed']
|
143 |
-
controlnet_conditioning_scale = params['controlnet_conditioning_scale']
|
144 |
-
control_mode = params['control_mode']
|
145 |
-
|
146 |
-
# Generate the holiday card using the current parameters
|
147 |
-
generated_image, processed_image, _ = call_control_net_api(
|
148 |
-
uploaded_file, prompt, control_mode=control_mode,
|
149 |
-
guidance_scale=guidance_scale, num_inference_steps=num_inference_steps,
|
150 |
-
seed=seed, controlnet_conditioning_scale=controlnet_conditioning_scale
|
151 |
-
)
|
152 |
-
|
153 |
-
if generated_image is not None:
|
154 |
-
# Resize generated_image to match original_image size
|
155 |
-
generated_image = generated_image.resize(original_image.size)
|
156 |
-
|
157 |
-
# Create a copy of the generated image
|
158 |
-
final_image = generated_image.copy()
|
159 |
-
|
160 |
-
# Crop the selected portion of the original image
|
161 |
-
cropped_original = original_image.crop((x_pos, y_pos, x_pos + crop_width, y_pos + crop_height))
|
162 |
-
|
163 |
-
# Get the size of the cropped image
|
164 |
-
cropped_width, cropped_height = cropped_original.size
|
165 |
-
|
166 |
-
# Calculate the center of the generated image
|
167 |
-
center_x = (final_image.width - cropped_width) // 2
|
168 |
-
center_y = (final_image.height - cropped_height) // 2
|
169 |
-
|
170 |
-
# Paste the cropped portion of the original image onto the generated image at the calculated center
|
171 |
-
final_image.paste(cropped_original, (center_x, center_y))
|
172 |
-
|
173 |
-
# Display the final holiday card in one of the columns
|
174 |
-
columns[i].image(final_image, caption=f"Holiday Card {i + 1}", use_column_width=True)
|
175 |
-
else:
|
176 |
-
st.error(f"Failed to generate holiday card {i + 1}. Please try again.")
|
177 |
-
else:
|
178 |
-
st.warning("Please upload an image to get started.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pages/{2_Multiple_HolidayCard.py → 1_Part_A_-_Experimentation_Station.py}
RENAMED
@@ -6,6 +6,8 @@ from PIL import Image, ImageDraw
|
|
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')
|
@@ -68,28 +70,92 @@ def draw_crop_preview(image, x, y, width, height):
|
|
68 |
draw.rectangle([x, y, x + width, y + height], outline="red", width=2)
|
69 |
return image
|
70 |
|
71 |
-
st.title("Holiday Card Generator with ControlNet")
|
72 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
uploaded_file = st.file_uploader("Upload an image", type=["png", "jpg", "jpeg"])
|
|
|
74 |
|
75 |
if uploaded_file is not None:
|
76 |
-
|
|
|
|
|
|
|
|
|
77 |
st.image(original_image, caption="Uploaded Image", use_column_width=True)
|
78 |
|
79 |
img_width, img_height = original_image.size
|
80 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
col1, col2 = st.columns(2)
|
82 |
with col1:
|
83 |
-
x_pos = st.slider("X position", 0, img_width, img_width // 4
|
84 |
-
|
|
|
|
|
85 |
with col2:
|
86 |
-
y_pos = st.slider("Y position", 0, img_height, img_height // 4
|
87 |
-
|
|
|
|
|
88 |
|
89 |
preview_image = draw_crop_preview(original_image.copy(), x_pos, y_pos, crop_width, crop_height)
|
90 |
st.image(preview_image, caption="Crop Preview", use_column_width=True)
|
|
|
91 |
|
92 |
-
st.subheader("Set
|
|
|
|
|
|
|
|
|
|
|
93 |
|
94 |
# Define the list of suggested holiday prompts
|
95 |
holiday_prompts = [
|
@@ -101,33 +167,49 @@ if uploaded_file is not None:
|
|
101 |
]
|
102 |
|
103 |
# Define input fields for each holiday card's parameters in expanders
|
104 |
-
card_params = []
|
105 |
for i in range(4):
|
106 |
with st.expander(f"Holiday Card {i + 1} Parameters"):
|
107 |
st.write(f"### Holiday Card {i + 1}")
|
108 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
109 |
# Dropdown to choose a suggested holiday prompt or enter custom prompt
|
110 |
selected_prompt = st.selectbox(f"Choose a holiday-themed prompt for Holiday Card {i + 1}", options=["Custom"] + holiday_prompts)
|
111 |
-
custom_prompt = st.text_input(f"Enter custom prompt for Holiday Card {i + 1}", value=
|
112 |
|
113 |
-
# Parameter sliders for each holiday card
|
114 |
-
guidance_scale = st.slider(f"Guidance Scale for Holiday Card {i + 1}", min_value=0.0, max_value=20.0, value=
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
|
|
|
|
|
|
|
|
|
|
119 |
|
120 |
-
#
|
121 |
-
card_params
|
122 |
"prompt": custom_prompt,
|
123 |
"guidance_scale": guidance_scale,
|
124 |
"num_inference_steps": num_inference_steps,
|
125 |
"seed": seed,
|
126 |
"controlnet_conditioning_scale": controlnet_conditioning_scale,
|
127 |
"control_mode": control_mode
|
128 |
-
}
|
|
|
|
|
129 |
|
130 |
# Generate the holiday cards
|
|
|
131 |
if st.button("Generate Holiday Cards"):
|
132 |
with st.spinner("Processing..."):
|
133 |
# Create a column layout for displaying cards side by side
|
@@ -135,8 +217,12 @@ if uploaded_file is not None:
|
|
135 |
col3, col4 = st.columns(2)
|
136 |
columns = [col1, col2, col3, col4] # To display images in a 2x2 grid
|
137 |
|
|
|
|
|
|
|
|
|
138 |
# Loop through each card's parameters and generate the holiday card
|
139 |
-
for i, params in enumerate(card_params):
|
140 |
prompt = params['prompt']
|
141 |
guidance_scale = params['guidance_scale']
|
142 |
num_inference_steps = params['num_inference_steps']
|
@@ -146,7 +232,7 @@ if uploaded_file is not None:
|
|
146 |
|
147 |
# Generate the holiday card using the current parameters
|
148 |
generated_image, processed_image, _ = call_control_net_api(
|
149 |
-
uploaded_file, prompt, control_mode=control_mode,
|
150 |
guidance_scale=guidance_scale, num_inference_steps=num_inference_steps,
|
151 |
seed=seed, controlnet_conditioning_scale=controlnet_conditioning_scale
|
152 |
)
|
@@ -171,6 +257,29 @@ if uploaded_file is not None:
|
|
171 |
# Paste the cropped portion of the original image onto the generated image at the calculated center
|
172 |
final_image.paste(cropped_original, (center_x, center_y))
|
173 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
174 |
# Display the final holiday card in one of the columns
|
175 |
columns[i].image(final_image, caption=f"Holiday Card {i + 1}", use_column_width=True)
|
176 |
|
@@ -183,6 +292,81 @@ if uploaded_file is not None:
|
|
183 |
columns[i].write(f"**Control Mode:** {control_mode}")
|
184 |
else:
|
185 |
st.error(f"Failed to generate holiday card {i + 1}. Please try again.")
|
186 |
-
else:
|
187 |
-
st.warning("Please upload an image to get started.")
|
188 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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')
|
|
|
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 = [
|
|
|
167 |
]
|
168 |
|
169 |
# Define input fields for each holiday card's parameters in expanders
|
|
|
170 |
for i in range(4):
|
171 |
with st.expander(f"Holiday Card {i + 1} Parameters"):
|
172 |
st.write(f"### Holiday Card {i + 1}")
|
173 |
|
174 |
+
# Get the card params from session_state if they exist
|
175 |
+
card_params = st.session_state.card_params[i]
|
176 |
+
card_params.setdefault("prompt", f"Custom Prompt {i + 1}")
|
177 |
+
card_params.setdefault("guidance_scale", 3.5)
|
178 |
+
card_params.setdefault("num_inference_steps", 30)
|
179 |
+
card_params.setdefault("seed", i * 100)
|
180 |
+
card_params.setdefault("controlnet_conditioning_scale", 0.5)
|
181 |
+
card_params.setdefault("control_mode", 0)
|
182 |
+
|
183 |
# Dropdown to choose a suggested holiday prompt or enter custom prompt
|
184 |
selected_prompt = st.selectbox(f"Choose a holiday-themed prompt for Holiday Card {i + 1}", options=["Custom"] + holiday_prompts)
|
185 |
+
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
|
186 |
|
187 |
+
# Parameter sliders for each holiday card with tooltips
|
188 |
+
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,
|
189 |
+
help="Adjusts how strongly the model follows the prompt. Higher values mean stronger adherence to the prompt.")
|
190 |
+
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,
|
191 |
+
help="More inference steps can lead to a higher quality image but will take longer to generate.")
|
192 |
+
seed = st.slider(f"Random Seed for Holiday Card {i + 1}", min_value=0, max_value=1000, value=card_params["seed"],
|
193 |
+
help="The seed value allows you to recreate the same image each time, or explore variations by changing the seed.")
|
194 |
+
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,
|
195 |
+
help="Controls how much the ControlNet input influences the output. A lower value reduces its influence.")
|
196 |
+
control_mode = st.slider(f"Control Mode for Holiday Card {i + 1}", min_value=0, max_value=2, value=card_params["control_mode"],
|
197 |
+
help="Choose how much ControlNet should influence the final image. 0: None, 1: Partial, 2: Full.")
|
198 |
|
199 |
+
# Update session state with the latest parameters
|
200 |
+
st.session_state.card_params[i] = {
|
201 |
"prompt": custom_prompt,
|
202 |
"guidance_scale": guidance_scale,
|
203 |
"num_inference_steps": num_inference_steps,
|
204 |
"seed": seed,
|
205 |
"controlnet_conditioning_scale": controlnet_conditioning_scale,
|
206 |
"control_mode": control_mode
|
207 |
+
}
|
208 |
+
|
209 |
+
st.divider() # Add a divider before the "Generate" button
|
210 |
|
211 |
# Generate the holiday cards
|
212 |
+
st.subheader("Just hit play!")
|
213 |
if st.button("Generate Holiday Cards"):
|
214 |
with st.spinner("Processing..."):
|
215 |
# Create a column layout for displaying cards side by side
|
|
|
217 |
col3, col4 = st.columns(2)
|
218 |
columns = [col1, col2, col3, col4] # To display images in a 2x2 grid
|
219 |
|
220 |
+
# Store image bytes and metadata in lists
|
221 |
+
image_files = []
|
222 |
+
metadata = []
|
223 |
+
|
224 |
# Loop through each card's parameters and generate the holiday card
|
225 |
+
for i, params in enumerate(st.session_state.card_params):
|
226 |
prompt = params['prompt']
|
227 |
guidance_scale = params['guidance_scale']
|
228 |
num_inference_steps = params['num_inference_steps']
|
|
|
232 |
|
233 |
# Generate the holiday card using the current parameters
|
234 |
generated_image, processed_image, _ = call_control_net_api(
|
235 |
+
st.session_state.uploaded_file, prompt, control_mode=control_mode,
|
236 |
guidance_scale=guidance_scale, num_inference_steps=num_inference_steps,
|
237 |
seed=seed, controlnet_conditioning_scale=controlnet_conditioning_scale
|
238 |
)
|
|
|
257 |
# Paste the cropped portion of the original image onto the generated image at the calculated center
|
258 |
final_image.paste(cropped_original, (center_x, center_y))
|
259 |
|
260 |
+
# Save image to BytesIO
|
261 |
+
img_byte_arr = BytesIO()
|
262 |
+
final_image.save(img_byte_arr, format="PNG")
|
263 |
+
img_byte_arr.seek(0)
|
264 |
+
image_files.append((f"holiday_card_{i + 1}.png", img_byte_arr))
|
265 |
+
|
266 |
+
# Store metadata
|
267 |
+
metadata.append({
|
268 |
+
"Card": f"Holiday Card {i + 1}",
|
269 |
+
"Prompt": prompt,
|
270 |
+
"Guidance Scale": guidance_scale,
|
271 |
+
"Inference Steps": num_inference_steps,
|
272 |
+
"Seed": seed,
|
273 |
+
"ControlNet Conditioning Scale": controlnet_conditioning_scale,
|
274 |
+
"Control Mode": control_mode
|
275 |
+
})
|
276 |
+
|
277 |
+
# Persist image and metadata in session state
|
278 |
+
st.session_state.generated_cards[i] = {
|
279 |
+
"image": final_image,
|
280 |
+
"metadata": metadata[-1]
|
281 |
+
}
|
282 |
+
|
283 |
# Display the final holiday card in one of the columns
|
284 |
columns[i].image(final_image, caption=f"Holiday Card {i + 1}", use_column_width=True)
|
285 |
|
|
|
292 |
columns[i].write(f"**Control Mode:** {control_mode}")
|
293 |
else:
|
294 |
st.error(f"Failed to generate holiday card {i + 1}. Please try again.")
|
|
|
|
|
295 |
|
296 |
+
# Create ZIP file for download
|
297 |
+
if image_files:
|
298 |
+
zip_buffer = BytesIO()
|
299 |
+
with zipfile.ZipFile(zip_buffer, "w") as zf:
|
300 |
+
# Add images to the ZIP file
|
301 |
+
for file_name, img_data in image_files:
|
302 |
+
zf.writestr(file_name, img_data.getvalue())
|
303 |
+
|
304 |
+
# Add metadata to the ZIP file
|
305 |
+
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])
|
306 |
+
zf.writestr("metadata.txt", metadata_str)
|
307 |
+
|
308 |
+
zip_buffer.seek(0)
|
309 |
+
st.subheader("Step 4: Download & Share Your Masterpiece! 📥")
|
310 |
+
st.markdown("""
|
311 |
+
- Once you're happy with your card, simply hit the download button to save your card and message as a **PNG** image.
|
312 |
+
- You can also view and download any **previously generated holiday cards** from this session!
|
313 |
+
""")
|
314 |
+
st.download_button(
|
315 |
+
label="Download all images and metadata as ZIP",
|
316 |
+
data=zip_buffer,
|
317 |
+
file_name="holiday_cards.zip",
|
318 |
+
mime="application/zip"
|
319 |
+
)
|
320 |
+
|
321 |
+
# Display previously generated cards if they exist and create a ZIP download button
|
322 |
+
st.divider()
|
323 |
+
st.subheader("Previously Generated Holiday Cards")
|
324 |
+
if any(st.session_state.generated_cards):
|
325 |
+
col1, col2 = st.columns(2)
|
326 |
+
col3, col4 = st.columns(2)
|
327 |
+
columns = [col1, col2, col3, col4] # Display in 2x2 grid
|
328 |
+
|
329 |
+
image_files = []
|
330 |
+
metadata = [] # Ensure metadata is initialized as a list
|
331 |
+
|
332 |
+
# Loop through previously generated cards
|
333 |
+
for i, card in enumerate(st.session_state.generated_cards):
|
334 |
+
if card and "metadata" in card: # Ensure the card exists and has metadata
|
335 |
+
columns[i].image(card['image'], caption=f"Holiday Card {i + 1} (Saved)")
|
336 |
+
card_metadata = card['metadata'] # Retrieve the metadata for the current card
|
337 |
+
columns[i].write(f"**Prompt:** {card_metadata['Prompt']}")
|
338 |
+
columns[i].write(f"**Guidance Scale:** {card_metadata['Guidance Scale']}")
|
339 |
+
columns[i].write(f"**Inference Steps:** {card_metadata['Inference Steps']}")
|
340 |
+
columns[i].write(f"**Seed:** {card_metadata['Seed']}")
|
341 |
+
columns[i].write(f"**ControlNet Conditioning Scale:** {card_metadata['ControlNet Conditioning Scale']}")
|
342 |
+
columns[i].write(f"**Control Mode:** {card_metadata['Control Mode']}")
|
343 |
+
|
344 |
+
# Add each image and its metadata to prepare for ZIP download
|
345 |
+
img_byte_arr = BytesIO()
|
346 |
+
card['image'].save(img_byte_arr, format="PNG")
|
347 |
+
img_byte_arr.seek(0)
|
348 |
+
image_files.append((f"holiday_card_{i + 1}.png", img_byte_arr))
|
349 |
+
metadata.append(card_metadata) # Append card's metadata to the metadata list
|
350 |
+
|
351 |
+
# Provide a ZIP download button for previously generated cards
|
352 |
+
if image_files:
|
353 |
+
zip_buffer = BytesIO()
|
354 |
+
with zipfile.ZipFile(zip_buffer, "w") as zf:
|
355 |
+
# Add images to the ZIP file
|
356 |
+
for file_name, img_data in image_files:
|
357 |
+
zf.writestr(file_name, img_data.getvalue())
|
358 |
+
|
359 |
+
# Add metadata to the ZIP file
|
360 |
+
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])
|
361 |
+
zf.writestr("metadata.txt", metadata_str)
|
362 |
+
|
363 |
+
# """ zip_buffer.seek(0)
|
364 |
+
# st.divider()
|
365 |
+
# st.subheader("Save images & metadata")
|
366 |
+
# st.download_button(
|
367 |
+
# label="Download all previously generated images and metadata as ZIP",
|
368 |
+
# data=zip_buffer,
|
369 |
+
# file_name="holiday_cards.zip",
|
370 |
+
# mime="application/zip"
|
371 |
+
# )
|
372 |
+
# """
|
pages/2_Customize_HolidayCard.py
DELETED
@@ -1,322 +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
|
6 |
-
import numpy as np
|
7 |
-
import os
|
8 |
-
from dotenv import load_dotenv
|
9 |
-
from datetime import datetime
|
10 |
-
import json
|
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 create_control_image(original_image, x_pos, y_pos, crop_width, crop_height):
|
32 |
-
cropped_area = original_image.crop((x_pos, y_pos, x_pos + crop_width, y_pos + crop_height))
|
33 |
-
pad_width = int(crop_width * 0.2)
|
34 |
-
pad_height = int(crop_height * 0.2)
|
35 |
-
padded_width = crop_width + 2 * pad_width
|
36 |
-
padded_height = crop_height + 2 * pad_height
|
37 |
-
control_image = Image.new('RGB', (padded_width, padded_height), (128, 128, 128))
|
38 |
-
control_image.paste(cropped_area, (pad_width, pad_height))
|
39 |
-
return control_image
|
40 |
-
|
41 |
-
def process_image(image):
|
42 |
-
gray_image = image.convert('L')
|
43 |
-
np_image = np.array(gray_image)
|
44 |
-
edges = cv2.Canny(np_image, 100, 200)
|
45 |
-
edges_rgb = cv2.cvtColor(edges, cv2.COLOR_GRAY2RGB)
|
46 |
-
edge_image = Image.fromarray(edges_rgb)
|
47 |
-
byte_arr = BytesIO()
|
48 |
-
edge_image.save(byte_arr, format='JPEG')
|
49 |
-
byte_arr.seek(0)
|
50 |
-
return byte_arr, edge_image
|
51 |
-
|
52 |
-
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):
|
53 |
-
processed_image_bytes, processed_image = process_image(control_image)
|
54 |
-
files = {'control_image': ('control_image.jpg', processed_image_bytes, 'image/jpeg')}
|
55 |
-
|
56 |
-
width, height = control_image.size
|
57 |
-
aspect_ratio = get_closest_aspect_ratio(width, height)
|
58 |
-
|
59 |
-
data = {
|
60 |
-
'prompt': prompt,
|
61 |
-
'control_mode': control_mode,
|
62 |
-
'aspect_ratio': aspect_ratio,
|
63 |
-
'guidance_scale': guidance_scale,
|
64 |
-
'num_inference_steps': num_inference_steps,
|
65 |
-
'seed': seed,
|
66 |
-
'controlnet_conditioning_scale': controlnet_conditioning_scale
|
67 |
-
}
|
68 |
-
|
69 |
-
headers = {
|
70 |
-
'accept': 'image/jpeg',
|
71 |
-
'authorization': f'Bearer {api_key}',
|
72 |
-
}
|
73 |
-
|
74 |
-
response = requests.post(
|
75 |
-
'https://api.fireworks.ai/inference/v1/workflows/accounts/fireworks/models/flux-1-dev-controlnet-union/control_net',
|
76 |
-
files=files, data=data, headers=headers
|
77 |
-
)
|
78 |
-
|
79 |
-
if response.status_code == 200:
|
80 |
-
return Image.open(BytesIO(response.content)), processed_image
|
81 |
-
else:
|
82 |
-
st.error(f"Request failed with status code: {response.status_code}, Response: {response.text}")
|
83 |
-
return None, None
|
84 |
-
|
85 |
-
def draw_crop_preview(image, x, y, width, height):
|
86 |
-
draw = ImageDraw.Draw(image)
|
87 |
-
for i in range(5): # Draw multiple rectangles for thickness
|
88 |
-
draw.rectangle([x+i, y+i, x+width-i, y+height-i], outline="red")
|
89 |
-
return image
|
90 |
-
|
91 |
-
def create_placement_preview(generated_image, original_image, x_pos, y_pos, crop_width, crop_height, final_x_pos, final_y_pos, resize_factor):
|
92 |
-
expand_factor = 1.3
|
93 |
-
new_width = int(original_image.width * expand_factor)
|
94 |
-
new_height = int(original_image.height * expand_factor)
|
95 |
-
|
96 |
-
preview_image = generated_image.resize((new_width, new_height)).copy()
|
97 |
-
draw = ImageDraw.Draw(preview_image)
|
98 |
-
|
99 |
-
# Try to load a bold font, fall back to default if not available
|
100 |
-
try:
|
101 |
-
font = ImageFont.truetype("arialbd.ttf", 60) # Trying to use Arial Bold with larger size
|
102 |
-
except IOError:
|
103 |
-
try:
|
104 |
-
font = ImageFont.truetype("arial.ttf", 60) # Fallback to regular Arial
|
105 |
-
except IOError:
|
106 |
-
font = ImageFont.load_default()
|
107 |
-
|
108 |
-
# Function to draw text with background
|
109 |
-
def draw_text_with_background(draw, text, position, font, text_color, bg_color):
|
110 |
-
text_bbox = draw.textbbox(position, text, font=font)
|
111 |
-
draw.rectangle(text_bbox, fill=bg_color)
|
112 |
-
draw.text(position, text, font=font, fill=text_color)
|
113 |
-
|
114 |
-
# Draw rule of thirds lines with labels
|
115 |
-
for i in range(1, 3):
|
116 |
-
x = new_width * i // 3
|
117 |
-
y = new_height * i // 3
|
118 |
-
draw.line([(x, 0), (x, new_height)], fill=(255, 255, 0), width=5) # Even thicker vertical lines
|
119 |
-
draw.line([(0, y), (new_width, y)], fill=(255, 255, 0), width=5) # Even thicker horizontal lines
|
120 |
-
|
121 |
-
# Add labels with background
|
122 |
-
draw_text_with_background(draw, f"{i/3:.2f}", (x + 10, 20), font, (0, 0, 0), (255, 255, 255, 180))
|
123 |
-
draw_text_with_background(draw, f"{i/3:.2f}", (20, y + 10), font, (0, 0, 0), (255, 255, 255, 180))
|
124 |
-
|
125 |
-
# Draw center crosshair
|
126 |
-
center_x, center_y = new_width // 2, new_height // 2
|
127 |
-
crosshair_size = min(new_width, new_height) // 15
|
128 |
-
draw.line([(center_x - crosshair_size, center_y), (center_x + crosshair_size, center_y)], fill=(0, 255, 255), width=5)
|
129 |
-
draw.line([(center_x, center_y - crosshair_size), (center_x, center_y + crosshair_size)], fill=(0, 255, 255), width=5)
|
130 |
-
draw_text_with_background(draw, "0.50", (center_x + 10, center_y + 10), font, (0, 0, 0), (255, 255, 255, 180))
|
131 |
-
|
132 |
-
# Add more position indicators
|
133 |
-
for i in range(0, 11):
|
134 |
-
x = new_width * i // 10
|
135 |
-
y = new_height * i // 10
|
136 |
-
if i % 5 != 0: # Skip 0, 0.5, and 1 as they're already marked
|
137 |
-
draw_text_with_background(draw, f"{i/10:.2f}", (x + 10, 20), font, (0, 0, 0), (255, 255, 255, 180))
|
138 |
-
draw_text_with_background(draw, f"{i/10:.2f}", (20, y + 10), font, (0, 0, 0), (255, 255, 255, 180))
|
139 |
-
|
140 |
-
# Crop and resize the original image
|
141 |
-
cropped_original = original_image.crop((x_pos, y_pos, x_pos + crop_width, y_pos + crop_height))
|
142 |
-
new_crop_width = int(crop_width * resize_factor)
|
143 |
-
new_crop_height = int(crop_height * resize_factor)
|
144 |
-
cropped_original = cropped_original.resize((new_crop_width, new_crop_height), Image.LANCZOS)
|
145 |
-
|
146 |
-
# Calculate paste position
|
147 |
-
paste_x = int(final_x_pos * new_width)
|
148 |
-
paste_y = int(final_y_pos * new_height)
|
149 |
-
|
150 |
-
# Paste the cropped and resized original onto the preview
|
151 |
-
preview_image.paste(cropped_original, (paste_x, paste_y))
|
152 |
-
|
153 |
-
# Draw a border around the pasted area
|
154 |
-
for i in range(5): # Draw multiple rectangles for thickness
|
155 |
-
draw.rectangle([paste_x+i, paste_y+i, paste_x+new_crop_width-i, paste_y+new_crop_height-i], outline=(255, 0, 0), width=2)
|
156 |
-
|
157 |
-
return preview_image
|
158 |
-
|
159 |
-
def create_final_image(original_image, generated_image, x_pos, y_pos, crop_width, crop_height, final_x_pos, final_y_pos, resize_factor):
|
160 |
-
expand_factor = 1.3
|
161 |
-
new_width = int(original_image.width * expand_factor)
|
162 |
-
new_height = int(original_image.height * expand_factor)
|
163 |
-
|
164 |
-
final_image = generated_image.resize((new_width, new_height))
|
165 |
-
paste_x = int(final_x_pos * new_width)
|
166 |
-
paste_y = int(final_y_pos * new_height)
|
167 |
-
cropped_original = original_image.crop((x_pos, y_pos, x_pos + crop_width, y_pos + crop_height))
|
168 |
-
new_crop_width = int(crop_width * resize_factor)
|
169 |
-
new_crop_height = int(crop_height * resize_factor)
|
170 |
-
cropped_original = cropped_original.resize((new_crop_width, new_crop_height), Image.LANCZOS)
|
171 |
-
final_image.paste(cropped_original, (paste_x, paste_y))
|
172 |
-
|
173 |
-
return final_image
|
174 |
-
|
175 |
-
def create_run_folder():
|
176 |
-
folder_name = f"run_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
177 |
-
full_path = os.path.join(os.path.dirname(__file__), folder_name)
|
178 |
-
os.makedirs(full_path, exist_ok=True)
|
179 |
-
return full_path
|
180 |
-
|
181 |
-
def save_results(folder, original, generated, control, composite, metadata):
|
182 |
-
original.save(os.path.join(folder, "original.png"))
|
183 |
-
generated.save(os.path.join(folder, "generated.png"))
|
184 |
-
control.save(os.path.join(folder, "control.png"))
|
185 |
-
composite.save(os.path.join(folder, "composite.png"))
|
186 |
-
with open(os.path.join(folder, "metadata.json"), 'w') as f:
|
187 |
-
json.dump(metadata, f, indent=4)
|
188 |
-
|
189 |
-
# Streamlit UI
|
190 |
-
st.title("Holiday Card Generator with ControlNet")
|
191 |
-
|
192 |
-
uploaded_file = st.file_uploader("Upload an image", type=["png", "jpg", "jpeg"])
|
193 |
-
|
194 |
-
if uploaded_file is not None:
|
195 |
-
original_image = Image.open(uploaded_file)
|
196 |
-
st.image(original_image, caption="Uploaded Image", use_column_width=True)
|
197 |
-
|
198 |
-
img_width, img_height = original_image.size
|
199 |
-
|
200 |
-
st.subheader("Crop Selection")
|
201 |
-
col1, col2 = st.columns(2)
|
202 |
-
with col1:
|
203 |
-
x_pos = st.slider("X position", 0, img_width, img_width // 4)
|
204 |
-
crop_width = st.slider("Width", 10, img_width - x_pos, min(img_width // 2, img_width - x_pos))
|
205 |
-
with col2:
|
206 |
-
y_pos = st.slider("Y position", 0, img_height, img_height // 4)
|
207 |
-
crop_height = st.slider("Height", 10, img_height - y_pos, min(img_height // 2, img_height - y_pos))
|
208 |
-
|
209 |
-
preview_image = draw_crop_preview(original_image.copy(), x_pos, y_pos, crop_width, crop_height)
|
210 |
-
st.image(preview_image, caption="Crop Preview", use_column_width=True)
|
211 |
-
|
212 |
-
holiday_prompts = [
|
213 |
-
"A border of Festive snowflakes and winter patterns for a holiday card border",
|
214 |
-
"A border of Joyful Christmas ornaments and lights decorating the edges",
|
215 |
-
"A border of Warm and cozy fireplace scene with stockings and garlands",
|
216 |
-
"A border of Colorful Hanukkah menorahs and dreidels along the border",
|
217 |
-
"A border of New Year's Eve fireworks with stars and confetti framing the image"
|
218 |
-
]
|
219 |
-
|
220 |
-
selected_prompt = st.selectbox("Choose a holiday-themed prompt or enter your own", options=["Custom"] + holiday_prompts)
|
221 |
-
custom_prompt = st.text_input("Enter your custom prompt") if selected_prompt == "Custom" else ""
|
222 |
-
prompt = custom_prompt if selected_prompt == "Custom" else selected_prompt
|
223 |
-
|
224 |
-
with st.expander("Advanced Parameters"):
|
225 |
-
control_mode = st.slider("Control Mode", min_value=0, max_value=2, value=0)
|
226 |
-
controlnet_conditioning_scale = st.slider("ControlNet Conditioning Scale", min_value=0.0, max_value=1.0, value=0.5, step=0.1)
|
227 |
-
guidance_scale = st.slider("Guidance Scale", min_value=0.0, max_value=20.0, value=3.5, step=0.1)
|
228 |
-
num_inference_steps = st.slider("Number of Inference Steps", min_value=1, max_value=100, value=30, step=1)
|
229 |
-
seed = st.slider("Random Seed", min_value=0, max_value=1000, value=0)
|
230 |
-
|
231 |
-
if st.button("Generate Flux Image"):
|
232 |
-
if not prompt.strip():
|
233 |
-
st.error("Please enter a prompt.")
|
234 |
-
else:
|
235 |
-
with st.spinner("Generating Flux image..."):
|
236 |
-
control_image = create_control_image(original_image, x_pos, y_pos, crop_width, crop_height)
|
237 |
-
st.image(control_image, caption="Control Image (Cropped and Padded)", use_column_width=True)
|
238 |
-
|
239 |
-
generated_image, processed_image = call_control_net_api(
|
240 |
-
control_image, prompt, control_mode=control_mode,
|
241 |
-
guidance_scale=guidance_scale, num_inference_steps=num_inference_steps,
|
242 |
-
seed=seed, controlnet_conditioning_scale=controlnet_conditioning_scale
|
243 |
-
)
|
244 |
-
|
245 |
-
if generated_image is not None:
|
246 |
-
with st.expander("Edge Detection Result (Input to ControlNet)"):
|
247 |
-
st.image(processed_image, caption="Processed Edge Detection Image", use_column_width=True)
|
248 |
-
|
249 |
-
st.session_state.generated_image = generated_image
|
250 |
-
st.session_state.control_image = control_image
|
251 |
-
st.image(generated_image, caption="Generated Flux Image", use_column_width=True)
|
252 |
-
st.success("Flux image generated successfully! Now you can choose the final placement and size.")
|
253 |
-
else:
|
254 |
-
st.error("Failed to generate image. Please try again.")
|
255 |
-
|
256 |
-
if 'generated_image' in st.session_state:
|
257 |
-
st.subheader("Final Image Placement and Sizing")
|
258 |
-
|
259 |
-
# Radio button for centering or manual position
|
260 |
-
placement_option = st.radio(
|
261 |
-
"Choose placement mode:",
|
262 |
-
("Manual Position", "Auto Center")
|
263 |
-
)
|
264 |
-
|
265 |
-
# Resize factor (applied in both modes)
|
266 |
-
resize_factor = st.slider("Resize factor", 0.1, 2.0, 1.0, 0.1,
|
267 |
-
help="1.0 is original size, <1 is smaller, >1 is larger")
|
268 |
-
|
269 |
-
# Get dimensions of generated image and resized cropped image
|
270 |
-
generated_width, generated_height = st.session_state.generated_image.size
|
271 |
-
crop_width_resized = int(crop_width * resize_factor)
|
272 |
-
crop_height_resized = int(crop_height * resize_factor)
|
273 |
-
|
274 |
-
if placement_option == "Manual Position":
|
275 |
-
# Manual position: User selects final X & Y
|
276 |
-
col3, col4, col5 = st.columns(3)
|
277 |
-
with col3:
|
278 |
-
final_x_pos = st.slider("Final X position", 0.0, 1.0, 0.5, 0.01,
|
279 |
-
help="0 is left, 1 is right")
|
280 |
-
with col4:
|
281 |
-
final_y_pos = st.slider("Final Y position", 0.0, 1.0, 0.5, 0.01,
|
282 |
-
help="0 is top, 1 is bottom")
|
283 |
-
else:
|
284 |
-
# Auto-center: Calculate center positions, adjusting for the resized cropped image dimensions
|
285 |
-
final_x_pos = (generated_width - crop_width_resized) / 2 / generated_width
|
286 |
-
final_y_pos = (generated_height - crop_height_resized) / 2 / generated_height
|
287 |
-
|
288 |
-
# Generate placement preview with updated final X & Y positions and resize factor
|
289 |
-
placement_preview = create_placement_preview(st.session_state.generated_image, original_image,
|
290 |
-
x_pos, y_pos, crop_width, crop_height,
|
291 |
-
final_x_pos, final_y_pos, resize_factor)
|
292 |
-
st.image(placement_preview, caption="Placement and Size Preview (with numbered guidelines)", use_column_width=True)
|
293 |
-
st.info("Yellow lines show the rule of thirds. Cyan crosshair shows the center. White numbers indicate position values. Use these to help position your image.")
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
if st.button("Create Final Holiday Card"):
|
298 |
-
final_image = create_final_image(original_image, st.session_state.generated_image,
|
299 |
-
x_pos, y_pos, crop_width, crop_height,
|
300 |
-
final_x_pos, final_y_pos, resize_factor)
|
301 |
-
st.subheader("Final Holiday Card")
|
302 |
-
st.image(final_image, caption="Holiday Card Image with Custom Placement and Size", use_column_width=True)
|
303 |
-
|
304 |
-
# Save results
|
305 |
-
run_folder = create_run_folder()
|
306 |
-
metadata = {
|
307 |
-
"prompt": prompt,
|
308 |
-
"crop": {"x": x_pos, "y": y_pos, "width": crop_width, "height": crop_height},
|
309 |
-
"placement": {"x": final_x_pos, "y": final_y_pos},
|
310 |
-
"resize_factor": resize_factor,
|
311 |
-
"control_mode": control_mode,
|
312 |
-
"controlnet_conditioning_scale": controlnet_conditioning_scale,
|
313 |
-
"guidance_scale": guidance_scale,
|
314 |
-
"num_inference_steps": num_inference_steps,
|
315 |
-
"seed": seed
|
316 |
-
}
|
317 |
-
save_results(run_folder, original_image, st.session_state.generated_image,
|
318 |
-
st.session_state.control_image, final_image, metadata)
|
319 |
-
st.success(f"Results saved in folder: {run_folder}")
|
320 |
-
|
321 |
-
else:
|
322 |
-
st.warning("Please upload an image to get started.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pages/2_Part_B_-_Card_Generator.py
ADDED
@@ -0,0 +1,338 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 border",
|
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 |
+
]
|
216 |
+
st.subheader("⚙️ Step 3: Design Your Festive Border with Flux + Fireworks!")
|
217 |
+
st.markdown("""
|
218 |
+
- 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!** ✨
|
219 |
+
- 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**. """)
|
220 |
+
selected_prompt = st.selectbox("Choose a holiday-themed prompt or enter your own", options=["Custom"] + holiday_prompts)
|
221 |
+
custom_prompt = st.text_input("Enter your custom prompt") if selected_prompt == "Custom" else ""
|
222 |
+
prompt = custom_prompt if selected_prompt == "Custom" else selected_prompt
|
223 |
+
|
224 |
+
with st.expander("Advanced Parameters"):
|
225 |
+
control_mode = st.slider("Control Mode", min_value=0, max_value=2, value=0)
|
226 |
+
controlnet_conditioning_scale = st.slider("ControlNet Conditioning Scale", min_value=0.0, max_value=1.0, value=0.5, step=0.1)
|
227 |
+
guidance_scale = st.slider("Guidance Scale", min_value=0.0, max_value=20.0, value=3.5, step=0.1)
|
228 |
+
num_inference_steps = st.slider("Number of Inference Steps", min_value=1, max_value=100, value=30, step=1)
|
229 |
+
seed = st.slider("Random Seed", min_value=0, max_value=1000, value=0)
|
230 |
+
|
231 |
+
if st.button("Generate Holiday Card"):
|
232 |
+
if not prompt.strip():
|
233 |
+
st.error("Please enter a prompt.")
|
234 |
+
else:
|
235 |
+
with st.spinner("Processing..."):
|
236 |
+
generated_image, processed_image, _ = call_control_net_api(
|
237 |
+
st.session_state.uploaded_file, prompt, control_mode=control_mode,
|
238 |
+
guidance_scale=guidance_scale, num_inference_steps=num_inference_steps,
|
239 |
+
seed=seed, controlnet_conditioning_scale=controlnet_conditioning_scale
|
240 |
+
)
|
241 |
+
|
242 |
+
if generated_image is not None:
|
243 |
+
# Resize generated_image to match original_image size
|
244 |
+
generated_image = generated_image.resize(original_image.size)
|
245 |
+
|
246 |
+
# Create a copy of the generated image
|
247 |
+
final_image = generated_image.copy()
|
248 |
+
|
249 |
+
# Crop the selected portion of the original image
|
250 |
+
cropped_original = original_image.crop((x_pos, y_pos, x_pos + crop_width, y_pos + crop_height))
|
251 |
+
|
252 |
+
# Get the size of the cropped image
|
253 |
+
cropped_width, cropped_height = cropped_original.size
|
254 |
+
|
255 |
+
# Calculate the center of the generated image
|
256 |
+
center_x = (final_image.width - cropped_width) // 2
|
257 |
+
center_y = (final_image.height - cropped_height) // 2
|
258 |
+
|
259 |
+
# Paste the cropped portion of the original image onto the generated image at the calculated center
|
260 |
+
final_image.paste(cropped_original, (center_x, center_y))
|
261 |
+
|
262 |
+
# Save the generated image in session state for persistence
|
263 |
+
st.session_state.generated_image = final_image
|
264 |
+
|
265 |
+
# Save the image locally
|
266 |
+
final_image.save(f"{GENERATED_IMAGES_DIR}/holiday_card.png")
|
267 |
+
|
268 |
+
# Display the final holiday card
|
269 |
+
st.image(final_image, caption="Final Holiday Card", use_column_width=True)
|
270 |
+
else:
|
271 |
+
st.error("Failed to generate holiday card. Please try again.")
|
272 |
+
|
273 |
+
# Add a text box to input a custom message (limit to 280 characters like a tweet)
|
274 |
+
st.divider()
|
275 |
+
if 'generated_image' in st.session_state:
|
276 |
+
st.subheader("✏️ Step 4: Add Your Custom Holiday Message!")
|
277 |
+
st.markdown("""
|
278 |
+
- Add a fun, heartfelt, or witty holiday message to the bottom of your card. 🎉
|
279 |
+
- Choose from a variety of **festive fonts** and adjust the **font size** to make your message stand out! 🎅 """)
|
280 |
+
|
281 |
+
# Text input for custom message
|
282 |
+
custom_message = st.text_input("Add a custom holiday message to the card (max 280 characters)", max_chars=280)
|
283 |
+
|
284 |
+
# Font selection dropdown
|
285 |
+
selected_font = st.selectbox("Choose a font for your message", options=list(fonts.keys()))
|
286 |
+
|
287 |
+
# Slider to allow the user to adjust the font size
|
288 |
+
font_size = st.slider("Adjust font size", min_value=10, max_value=200, value=100, step=10)
|
289 |
+
|
290 |
+
# Get the corresponding font path
|
291 |
+
font_path = fonts[selected_font]
|
292 |
+
|
293 |
+
if st.button("Generate Holiday Card with Message"):
|
294 |
+
final_image_with_message = st.session_state.generated_image.copy()
|
295 |
+
|
296 |
+
# Get the dimensions of the cropped image and final image
|
297 |
+
cropped_bottom_y = y_pos + crop_height # y_pos is the top Y position of the cropped area
|
298 |
+
|
299 |
+
# Add the custom message to the image
|
300 |
+
if custom_message:
|
301 |
+
final_image_with_message = add_message_to_image(final_image_with_message, cropped_bottom_y, custom_message, font_path, font_size)
|
302 |
+
|
303 |
+
# Display the final holiday card with the message
|
304 |
+
st.image(final_image_with_message, caption="Holiday Card with Custom Message", use_column_width=True)
|
305 |
+
|
306 |
+
# Save image for download
|
307 |
+
img_byte_arr = BytesIO()
|
308 |
+
final_image_with_message.save(img_byte_arr, format="PNG")
|
309 |
+
img_byte_arr.seek(0)
|
310 |
+
|
311 |
+
# Download button for the final image
|
312 |
+
st.divider()
|
313 |
+
st.subheader("📥 Step 5: Download & Share Your Masterpiece!")
|
314 |
+
st.markdown("""
|
315 |
+
- Once you're happy with your card, simply hit the download button to save your card and message as a PNG image.
|
316 |
+
- You can also view and download any **previously generated holiday cards** from this session!""")
|
317 |
+
st.download_button(
|
318 |
+
label="Download Holiday Card with Message",
|
319 |
+
data=img_byte_arr,
|
320 |
+
file_name="holiday_card_with_message.png",
|
321 |
+
mime="image/png"
|
322 |
+
)
|
323 |
+
|
324 |
+
# Save image locally
|
325 |
+
final_image_with_message.save(f"{GENERATED_IMAGES_DIR}/holiday_card_with_message.png", format="PNG")
|
326 |
+
except OSError:
|
327 |
+
st.error("The uploaded image seems to be truncated or corrupted. Please try with a different image.")
|
328 |
+
else:
|
329 |
+
st.warning("Please upload an image to get started.")
|
330 |
+
|
331 |
+
# Load and display previously generated images
|
332 |
+
st.divider()
|
333 |
+
st.subheader("Previously Generated Holiday Cards")
|
334 |
+
|
335 |
+
saved_image_path = f"{GENERATED_IMAGES_DIR}/holiday_card_with_message.png"
|
336 |
+
if os.path.exists(saved_image_path):
|
337 |
+
saved_image = Image.open(saved_image_path)
|
338 |
+
st.image(saved_image, caption="Previously Generated Holiday Card", use_column_width=True)
|
pages/3_Pure_FLUX_ImageGeneration.py
DELETED
@@ -1,77 +0,0 @@
|
|
1 |
-
import os
|
2 |
-
import requests
|
3 |
-
from dotenv import load_dotenv
|
4 |
-
from PIL import Image
|
5 |
-
from io import BytesIO
|
6 |
-
import streamlit as st
|
7 |
-
|
8 |
-
# Set page configuration
|
9 |
-
st.set_page_config(page_title="FLUX Image Generation Tool", page_icon="🎇")
|
10 |
-
|
11 |
-
# Correct the path to the .env file to reflect its location
|
12 |
-
dotenv_path = os.path.join(os.path.dirname(__file__), '../env/.env')
|
13 |
-
|
14 |
-
# Load environment variables from the .env file
|
15 |
-
load_dotenv(dotenv_path, override=True)
|
16 |
-
|
17 |
-
# Get the Fireworks API key from the .env file
|
18 |
-
fireworks_api_key = os.getenv("FIREWORKS_API_KEY")
|
19 |
-
|
20 |
-
if not fireworks_api_key:
|
21 |
-
st.error("API key not found. Make sure FIREWORKS_API_KEY is set in the .env file.")
|
22 |
-
|
23 |
-
# Function to make requests to the FLUX models
|
24 |
-
def generate_flux_image(model_path, prompt, steps):
|
25 |
-
url = f"https://api.fireworks.ai/inference/v1/workflows/accounts/fireworks/models/{model_path}/text_to_image"
|
26 |
-
headers = {
|
27 |
-
"Authorization": f"Bearer {fireworks_api_key}",
|
28 |
-
"Content-Type": "application/json",
|
29 |
-
"Accept": "image/jpeg"
|
30 |
-
}
|
31 |
-
data = {
|
32 |
-
"prompt": prompt,
|
33 |
-
"aspect_ratio": "16:9",
|
34 |
-
"guidance_scale": 3.5,
|
35 |
-
"num_inference_steps": steps,
|
36 |
-
"seed": 0
|
37 |
-
}
|
38 |
-
|
39 |
-
# Send the request
|
40 |
-
response = requests.post(url, headers=headers, json=data)
|
41 |
-
|
42 |
-
if response.status_code == 200:
|
43 |
-
# Convert the response to an image
|
44 |
-
img_data = response.content
|
45 |
-
img = Image.open(BytesIO(img_data))
|
46 |
-
return img
|
47 |
-
else:
|
48 |
-
raise RuntimeError(f"Error with FLUX model {model_path}: {response.text}")
|
49 |
-
|
50 |
-
# Streamlit UI
|
51 |
-
st.title("FLUX Image Generation")
|
52 |
-
st.write("Generate images using the FLUX models.")
|
53 |
-
|
54 |
-
# User input for the prompt
|
55 |
-
prompt = st.text_input("Enter your prompt for image generation:")
|
56 |
-
|
57 |
-
# Dropdown to select the model
|
58 |
-
model_choice = st.selectbox("Select the model:", ["flux-1-dev", "flux-1-schnell"])
|
59 |
-
|
60 |
-
# Button to generate images
|
61 |
-
if st.button("Generate Image"):
|
62 |
-
if not prompt.strip():
|
63 |
-
st.error("Please provide a prompt.")
|
64 |
-
else:
|
65 |
-
try:
|
66 |
-
with st.spinner("Generating image..."):
|
67 |
-
# Determine steps based on model
|
68 |
-
steps = 30 if model_choice == "flux-1-dev" else 4
|
69 |
-
|
70 |
-
# Generate image
|
71 |
-
generated_image = generate_flux_image(model_choice, prompt, steps)
|
72 |
-
|
73 |
-
# Display the image
|
74 |
-
st.image(generated_image, caption=f"Generated using {model_choice}", use_column_width=True)
|
75 |
-
|
76 |
-
except Exception as e:
|
77 |
-
st.error(f"An error occurred: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|