Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -1,306 +1,35 @@
|
|
1 |
-
import gradio as gr
|
2 |
-
import replicate
|
3 |
import os
|
4 |
-
|
5 |
-
import
|
6 |
-
from
|
7 |
|
8 |
-
|
9 |
-
replicate_client = replicate.Client(api_token=os.getenv("RAPI_TOKEN"))
|
10 |
-
|
11 |
-
# Comprehensive hairstyle options - All 93 styles
|
12 |
-
HAIRCUT_OPTIONS = [
|
13 |
-
"Crew Cut", "Faux Hawk", "Slicked Back", "Side-Parted", "Center-Parted",
|
14 |
-
"Blunt Bangs", "Side-Swept Bangs", "Shag", "Lob", "Angled Bob",
|
15 |
-
"A-Line Bob", "Asymmetrical Bob", "Graduated Bob", "Inverted Bob", "Layered Shag",
|
16 |
-
"Choppy Layers", "Razor Cut", "Perm", "Ombré", "Straightened",
|
17 |
-
"Soft Waves", "Glamorous Waves", "Hollywood Waves", "Finger Waves", "Tousled",
|
18 |
-
"Feathered", "Pageboy", "Pigtails", "Pin Curls", "Rollerset",
|
19 |
-
"Twist Out", "Bantu Knots", "Dreadlocks", "Cornrows", "Box Braids",
|
20 |
-
"Crochet Braids", "Double Dutch Braids", "French Fishtail Braid", "Waterfall Braid", "Rope Braid",
|
21 |
-
"Heart Braid", "Halo Braid", "Crown Braid", "Braided Crown", "Bubble Braid",
|
22 |
-
"Bubble Ponytail", "Ballerina Braids", "Milkmaid Braids", "Bohemian Braids", "Flat Twist",
|
23 |
-
"Crown Twist", "Twisted Bun", "Twisted Half-Updo", "Twist and Pin Updo", "Chignon",
|
24 |
-
"Simple Chignon", "Messy Chignon", "French Twist", "French Twist Updo", "French Roll",
|
25 |
-
"Updo", "Messy Updo", "Knotted Updo", "Ballerina Bun", "Banana Clip Updo",
|
26 |
-
"Beehive", "Bouffant", "Hair Bow", "Half-Up Top Knot", "Half-Up, Half-Down",
|
27 |
-
"Messy Bun with a Headband", "Messy Bun with a Scarf", "Messy Fishtail Braid", "Sideswept Pixie", "Mohawk Fade",
|
28 |
-
"Straight", "Wavy", "Curly", "Bob", "Pixie Cut",
|
29 |
-
"Layered", "Messy Bun", "High Ponytail", "Low Ponytail", "Braided Ponytail",
|
30 |
-
"French Braid", "Dutch Braid", "Fishtail Braid", "Space Buns", "Top Knot",
|
31 |
-
"Undercut", "Mohawk"
|
32 |
-
]
|
33 |
-
|
34 |
-
# Hair color options with color codes
|
35 |
-
HAIR_COLOR_OPTIONS = {
|
36 |
-
"Blonde": "#F4E4C1",
|
37 |
-
"Brunette": "#6F4E37",
|
38 |
-
"Black": "#1C1C1C",
|
39 |
-
"Dark Brown": "#3B2F2F",
|
40 |
-
"Medium Brown": "#8B4513",
|
41 |
-
"Light Brown": "#A0826D",
|
42 |
-
"Auburn": "#A52A2A",
|
43 |
-
"Copper": "#B87333",
|
44 |
-
"Red": "#DC143C",
|
45 |
-
"Strawberry Blonde": "#FFB6C1",
|
46 |
-
"Platinum Blonde": "#FAFAD2",
|
47 |
-
"Silver": "#C0C0C0",
|
48 |
-
"White": "#FFFFFF",
|
49 |
-
"Blue": "#4169E1",
|
50 |
-
"Purple": "#9370DB",
|
51 |
-
"Pink": "#FF69B4",
|
52 |
-
"Green": "#228B22",
|
53 |
-
"Blue-Black": "#1C1C3D",
|
54 |
-
"Golden Blonde": "#FFD700",
|
55 |
-
"Honey Blonde": "#F0E68C",
|
56 |
-
"Caramel": "#C68E17",
|
57 |
-
"Chestnut": "#954535",
|
58 |
-
"Mahogany": "#C04000",
|
59 |
-
"Burgundy": "#800020",
|
60 |
-
"Jet Black": "#0A0A0A",
|
61 |
-
"Ash Brown": "#8B7355",
|
62 |
-
"Ash Blonde": "#D3D3D3",
|
63 |
-
"Titanium": "#878787",
|
64 |
-
"Rose Gold": "#E0BFB8"
|
65 |
-
}
|
66 |
-
|
67 |
-
def create_color_palette_html():
|
68 |
-
"""Create HTML for visual color palette"""
|
69 |
-
html = '''
|
70 |
-
<style>
|
71 |
-
.color-grid {
|
72 |
-
display: grid;
|
73 |
-
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
|
74 |
-
gap: 8px;
|
75 |
-
padding: 10px;
|
76 |
-
max-height: 300px;
|
77 |
-
overflow-y: auto;
|
78 |
-
border: 1px solid #e0e0e0;
|
79 |
-
border-radius: 8px;
|
80 |
-
background-color: #f9f9f9;
|
81 |
-
}
|
82 |
-
.color-cell {
|
83 |
-
aspect-ratio: 1;
|
84 |
-
border-radius: 8px;
|
85 |
-
cursor: pointer;
|
86 |
-
transition: all 0.3s ease;
|
87 |
-
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
|
88 |
-
display: flex;
|
89 |
-
align-items: flex-end;
|
90 |
-
justify-content: center;
|
91 |
-
overflow: hidden;
|
92 |
-
border: 3px solid transparent;
|
93 |
-
}
|
94 |
-
.color-cell:hover {
|
95 |
-
transform: translateY(-2px);
|
96 |
-
box-shadow: 0 4px 10px rgba(0,0,0,0.3);
|
97 |
-
}
|
98 |
-
.color-label {
|
99 |
-
width: 100%;
|
100 |
-
background-color: rgba(0,0,0,0.7);
|
101 |
-
color: white;
|
102 |
-
font-size: 9px;
|
103 |
-
padding: 2px;
|
104 |
-
text-align: center;
|
105 |
-
font-weight: bold;
|
106 |
-
}
|
107 |
-
</style>
|
108 |
-
<div class="color-grid">
|
109 |
-
'''
|
110 |
-
|
111 |
-
for color_name, color_code in HAIR_COLOR_OPTIONS.items():
|
112 |
-
html += f'''
|
113 |
-
<div class="color-cell" style="background-color: {color_code};" title="{color_name}">
|
114 |
-
<span class="color-label">{color_name}</span>
|
115 |
-
</div>
|
116 |
-
'''
|
117 |
-
|
118 |
-
html += '</div>'
|
119 |
-
return html
|
120 |
-
|
121 |
-
def update_selected_color(color_name):
|
122 |
-
"""Update the selected color display"""
|
123 |
-
return f"**Selected Color:** {color_name}", color_name
|
124 |
-
|
125 |
-
def change_haircut(input_image, haircut_style, hair_color):
|
126 |
-
"""
|
127 |
-
Process image through API to change hairstyle
|
128 |
-
"""
|
129 |
try:
|
130 |
-
#
|
131 |
-
|
132 |
-
return None, "Please upload an image first."
|
133 |
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
|
138 |
-
|
|
|
|
|
|
|
139 |
|
140 |
-
#
|
141 |
-
|
142 |
-
input_image.save(temp_path)
|
143 |
|
144 |
-
#
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
"hair_color": hair_color,
|
150 |
-
"input_image": open(temp_path, "rb")
|
151 |
-
}
|
152 |
-
)
|
153 |
-
|
154 |
-
# Process results
|
155 |
-
if output:
|
156 |
-
# If output is URL
|
157 |
-
if isinstance(output, str):
|
158 |
-
response = requests.get(output)
|
159 |
-
result_image = Image.open(BytesIO(response.content))
|
160 |
-
# If output is file object
|
161 |
-
else:
|
162 |
-
result_image = Image.open(BytesIO(output.read()))
|
163 |
-
|
164 |
-
# Clean up temporary file
|
165 |
-
if os.path.exists(temp_path):
|
166 |
-
os.remove(temp_path)
|
167 |
-
|
168 |
-
return result_image, f"Successfully applied {haircut_style} style with {hair_color} color!"
|
169 |
-
else:
|
170 |
-
return None, "Processing error occurred."
|
171 |
|
172 |
except Exception as e:
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
return None, f"Error: {str(e)}"
|
177 |
-
|
178 |
-
# Create Gradio interface
|
179 |
-
with gr.Blocks(title="AI Hairstyle Changer", theme=gr.themes.Soft()) as demo:
|
180 |
-
gr.Markdown(
|
181 |
-
"""
|
182 |
-
# 🎨 AI Hairstyle Changer
|
183 |
-
"""
|
184 |
-
)
|
185 |
-
|
186 |
-
with gr.Row():
|
187 |
-
with gr.Column(scale=1):
|
188 |
-
# Input section
|
189 |
-
input_image = gr.Image(
|
190 |
-
label="Upload Your Photo",
|
191 |
-
type="pil",
|
192 |
-
height=400
|
193 |
-
)
|
194 |
-
|
195 |
-
with gr.Group():
|
196 |
-
gr.Markdown("### Select Hair Color")
|
197 |
-
selected_color_display = gr.Markdown(value="**Selected Color:** Black")
|
198 |
-
|
199 |
-
# Dropdown for color selection (functional)
|
200 |
-
hair_color_dropdown = gr.Dropdown(
|
201 |
-
choices=list(HAIR_COLOR_OPTIONS.keys()),
|
202 |
-
value="Black",
|
203 |
-
label="Choose Hair Color",
|
204 |
-
info="Select from dropdown for color"
|
205 |
-
)
|
206 |
-
|
207 |
-
# Visual color palette (for reference)
|
208 |
-
gr.Markdown("**Color Palette Reference:**")
|
209 |
-
color_palette_display = gr.HTML(create_color_palette_html())
|
210 |
-
|
211 |
-
submit_btn = gr.Button(
|
212 |
-
"🎨 Apply Hairstyle",
|
213 |
-
variant="primary",
|
214 |
-
size="lg"
|
215 |
-
)
|
216 |
-
|
217 |
-
with gr.Column(scale=2):
|
218 |
-
# Hairstyle selection with scrollable radio buttons
|
219 |
-
with gr.Group():
|
220 |
-
gr.Markdown(f"### Select Hairstyle ({len(HAIRCUT_OPTIONS)} total styles)")
|
221 |
-
gr.Markdown("*Scroll down to see all 93 hairstyle options*")
|
222 |
-
haircut_radio = gr.Radio(
|
223 |
-
choices=HAIRCUT_OPTIONS,
|
224 |
-
value="Bob",
|
225 |
-
label="Hairstyle Options",
|
226 |
-
info="Scroll to browse all 93 hairstyle options"
|
227 |
-
)
|
228 |
-
|
229 |
-
with gr.Column(scale=1):
|
230 |
-
# Output section
|
231 |
-
output_image = gr.Image(
|
232 |
-
label="Result",
|
233 |
-
type="pil",
|
234 |
-
height=400
|
235 |
-
)
|
236 |
-
|
237 |
-
status_text = gr.Textbox(
|
238 |
-
label="Status",
|
239 |
-
interactive=False,
|
240 |
-
value="Upload a photo and select a hairstyle to begin."
|
241 |
-
)
|
242 |
-
|
243 |
-
# Update color display when dropdown changes
|
244 |
-
hair_color_dropdown.change(
|
245 |
-
fn=update_selected_color,
|
246 |
-
inputs=[hair_color_dropdown],
|
247 |
-
outputs=[selected_color_display, gr.State()]
|
248 |
-
)
|
249 |
-
|
250 |
-
# Button click event
|
251 |
-
submit_btn.click(
|
252 |
-
fn=change_haircut,
|
253 |
-
inputs=[input_image, haircut_radio, hair_color_dropdown],
|
254 |
-
outputs=[output_image, status_text]
|
255 |
-
)
|
256 |
-
|
257 |
-
# Style customization
|
258 |
-
demo.css = """
|
259 |
-
/* Radio button container */
|
260 |
-
#component-8 > div.wrap > div {
|
261 |
-
max-height: 600px !important;
|
262 |
-
overflow-y: auto !important;
|
263 |
-
padding: 10px;
|
264 |
-
border: 1px solid #e0e0e0;
|
265 |
-
border-radius: 8px;
|
266 |
-
background-color: #f9f9f9;
|
267 |
-
}
|
268 |
-
|
269 |
-
/* Style radio button options */
|
270 |
-
label.svelte-1gfkn6j {
|
271 |
-
display: block !important;
|
272 |
-
padding: 8px 12px !important;
|
273 |
-
margin: 4px 0 !important;
|
274 |
-
border-radius: 6px !important;
|
275 |
-
cursor: pointer !important;
|
276 |
-
transition: all 0.2s ease !important;
|
277 |
-
}
|
278 |
-
|
279 |
-
label.svelte-1gfkn6j:hover {
|
280 |
-
background-color: #e8e8e8 !important;
|
281 |
-
}
|
282 |
-
|
283 |
-
/* Selected radio option */
|
284 |
-
label.svelte-1gfkn6j.selected {
|
285 |
-
background-color: #2196F3 !important;
|
286 |
-
color: white !important;
|
287 |
-
}
|
288 |
-
"""
|
289 |
-
|
290 |
-
|
291 |
|
292 |
-
# Run the app
|
293 |
if __name__ == "__main__":
|
294 |
-
|
295 |
-
if not os.getenv("RAPI_TOKEN"):
|
296 |
-
print("⚠️ Warning: RAPI_TOKEN environment variable not set!")
|
297 |
-
print("Set it using: export RAPI_TOKEN='your_token'")
|
298 |
-
|
299 |
-
# Verify all styles are loaded
|
300 |
-
print(f"✅ Loaded {len(HAIRCUT_OPTIONS)} hairstyle options")
|
301 |
-
print(f"✅ Loaded {len(HAIR_COLOR_OPTIONS)} hair color options")
|
302 |
-
|
303 |
-
demo.launch(
|
304 |
-
share=True, # Create shareable link
|
305 |
-
debug=True # Debug mode
|
306 |
-
)
|
|
|
|
|
|
|
1 |
import os
|
2 |
+
import sys
|
3 |
+
import streamlit as st
|
4 |
+
from tempfile import NamedTemporaryFile
|
5 |
|
6 |
+
def main():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
try:
|
8 |
+
# Get the code from secrets
|
9 |
+
code = os.environ.get("MAIN_CODE")
|
|
|
10 |
|
11 |
+
if not code:
|
12 |
+
st.error("⚠️ The application code wasn't found in secrets. Please add the MAIN_CODE secret.")
|
13 |
+
return
|
14 |
|
15 |
+
# Create a temporary Python file
|
16 |
+
with NamedTemporaryFile(suffix='.py', delete=False, mode='w') as tmp:
|
17 |
+
tmp.write(code)
|
18 |
+
tmp_path = tmp.name
|
19 |
|
20 |
+
# Execute the code
|
21 |
+
exec(compile(code, tmp_path, 'exec'), globals())
|
|
|
22 |
|
23 |
+
# Clean up the temporary file
|
24 |
+
try:
|
25 |
+
os.unlink(tmp_path)
|
26 |
+
except:
|
27 |
+
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
|
29 |
except Exception as e:
|
30 |
+
st.error(f"⚠️ Error loading or executing the application: {str(e)}")
|
31 |
+
import traceback
|
32 |
+
st.code(traceback.format_exc())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
|
|
|
34 |
if __name__ == "__main__":
|
35 |
+
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|