ginipick commited on
Commit
d3fd436
·
verified ·
1 Parent(s): 71991e6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +105 -128
app.py CHANGED
@@ -5,9 +5,11 @@ from PIL import Image
5
  import requests
6
  from io import BytesIO
7
 
 
 
8
  replicate_client = replicate.Client(api_token=os.getenv("RAPI_TOKEN"))
9
 
10
- # Comprehensive hairstyle options
11
  HAIRCUT_OPTIONS = [
12
  "Crew Cut", "Faux Hawk", "Slicked Back", "Side-Parted", "Center-Parted",
13
  "Blunt Bangs", "Side-Swept Bangs", "Shag", "Lob", "Angled Bob",
@@ -63,33 +65,18 @@ HAIR_COLOR_OPTIONS = {
63
  "Rose Gold": "#E0BFB8"
64
  }
65
 
66
- def create_color_button(color_name, color_code):
67
- """Create HTML for a single color button"""
68
- return f'''
69
- <button class="color-btn" onclick="selectColor('{color_name}')" style="background-color: {color_code};" title="{color_name}">
70
- <span class="color-label">{color_name}</span>
71
- </button>
72
- '''
73
-
74
- def create_color_palette():
75
- """Create the complete color palette HTML"""
76
- palette_html = '<div class="color-palette">'
77
- for color_name, color_code in HAIR_COLOR_OPTIONS.items():
78
- palette_html += create_color_button(color_name, color_code)
79
- palette_html += '</div>'
80
- return palette_html
81
-
82
- def change_haircut(input_image, haircut_style, hair_color_state):
83
  """
84
- Process image through Replicate API to change hairstyle
85
  """
86
  try:
87
  # Check if image is provided
88
  if input_image is None:
89
- return None, "Please upload an image first.", hair_color_state
90
 
91
- # Use the color from state
92
- hair_color = hair_color_state if hair_color_state else "Black"
 
93
 
94
  # Save PIL Image to temporary file
95
  temp_path = "temp_input.png"
@@ -119,36 +106,23 @@ def change_haircut(input_image, haircut_style, hair_color_state):
119
  if os.path.exists(temp_path):
120
  os.remove(temp_path)
121
 
122
- return result_image, f"Successfully applied {haircut_style} style with {hair_color} color!", hair_color_state
123
  else:
124
- return None, "Processing error occurred.", hair_color_state
125
 
126
  except Exception as e:
127
  # Clean up temporary file on error
128
  if os.path.exists(temp_path):
129
  os.remove(temp_path)
130
- return None, f"Error: {str(e)}", hair_color_state
 
 
 
 
131
 
132
  # Create Gradio interface
133
  with gr.Blocks(title="AI Hairstyle Changer", theme=gr.themes.Soft()) as demo:
134
- # Hidden state for selected color
135
- selected_color = gr.State(value="Black")
136
-
137
- gr.Markdown(
138
- """
139
- # 🎨 Ginigen AI Hairstyle Changer
140
-
141
- Transform your hairstyle with AI! Upload a photo and choose from 90+ hairstyle options and 29 hair colors.
142
-
143
- **Instructions:**
144
- 1. Upload a front-facing photo with clear visibility of your face
145
- 2. Select your desired hairstyle from the options below
146
- 3. Click on a hair color from the palette
147
- 4. Click "Apply Hairstyle" to see the transformation
148
-
149
- ⚠️ **Note:** Requires Replicate API key. Set REPLICATE_API_TOKEN in your environment.
150
- """
151
- )
152
 
153
  with gr.Row():
154
  with gr.Column(scale=1):
@@ -162,7 +136,23 @@ with gr.Blocks(title="AI Hairstyle Changer", theme=gr.themes.Soft()) as demo:
162
  with gr.Group():
163
  gr.Markdown("### Select Hair Color")
164
  selected_color_display = gr.Markdown(value="**Selected Color:** Black")
165
- color_palette = gr.HTML(create_color_palette())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
 
167
  submit_btn = gr.Button(
168
  "🎨 Apply Hairstyle",
@@ -173,13 +163,13 @@ with gr.Blocks(title="AI Hairstyle Changer", theme=gr.themes.Soft()) as demo:
173
  with gr.Column(scale=2):
174
  # Hairstyle selection with scrollable radio buttons
175
  with gr.Group():
176
- gr.Markdown("### Select Hairstyle")
177
  with gr.Column(elem_classes="hairstyle-container"):
178
  haircut_radio = gr.Radio(
179
  choices=HAIRCUT_OPTIONS,
180
  value="Bob",
181
  label="Hairstyle Options",
182
- info="Browse and select from 90+ hairstyle options",
183
  elem_classes="hairstyle-radio"
184
  )
185
 
@@ -197,36 +187,8 @@ with gr.Blocks(title="AI Hairstyle Changer", theme=gr.themes.Soft()) as demo:
197
  value="Upload a photo and select a hairstyle to begin."
198
  )
199
 
200
- # JavaScript for color selection
201
- demo.load(None, None, None, js="""
202
- function() {
203
- window.selectColor = function(colorName) {
204
- // Update all color buttons to remove selected class
205
- document.querySelectorAll('.color-btn').forEach(btn => {
206
- btn.classList.remove('selected');
207
- });
208
-
209
- // Add selected class to clicked button
210
- event.target.closest('.color-btn').classList.add('selected');
211
-
212
- // Update the display text
213
- const displayElement = document.querySelector('#component-5 > div.prose.markdown-text');
214
- if (displayElement) {
215
- displayElement.innerHTML = '<strong>Selected Color:</strong> ' + colorName;
216
- }
217
-
218
- // Store the selected color
219
- window.selectedHairColor = colorName;
220
- };
221
-
222
- // Set initial selection
223
- window.selectedHairColor = 'Black';
224
- document.querySelector('.color-btn').classList.add('selected');
225
- }
226
- """)
227
-
228
- # Custom CSS
229
- demo.css = """
230
  .hairstyle-container {
231
  max-height: 600px;
232
  overflow-y: auto;
@@ -249,77 +211,92 @@ with gr.Blocks(title="AI Hairstyle Changer", theme=gr.themes.Soft()) as demo:
249
  background-color: #e8e8e8;
250
  }
251
 
252
- .color-palette {
253
- display: grid;
254
- grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
255
- gap: 8px;
256
- padding: 10px;
257
- max-height: 300px;
258
- overflow-y: auto;
259
- border: 1px solid #e0e0e0;
260
- border-radius: 8px;
261
- background-color: #f9f9f9;
262
  }
263
 
264
- .color-btn {
265
- position: relative;
266
- width: 100%;
267
- height: 50px;
268
- border: 3px solid transparent;
269
- border-radius: 8px;
270
- cursor: pointer;
271
- transition: all 0.3s ease;
272
- overflow: hidden;
273
- box-shadow: 0 2px 5px rgba(0,0,0,0.2);
274
- }
275
-
276
- .color-btn:hover {
277
- transform: translateY(-2px);
278
- box-shadow: 0 4px 10px rgba(0,0,0,0.3);
279
  }
280
 
281
- .color-btn.selected {
282
- border-color: #2196F3;
283
- transform: scale(1.05);
284
- box-shadow: 0 0 0 3px rgba(33, 150, 243, 0.3);
285
  }
 
286
 
287
- .color-label {
288
- position: absolute;
289
- bottom: 0;
290
- left: 0;
291
- right: 0;
292
- background-color: rgba(0,0,0,0.7);
293
- color: white;
294
- font-size: 10px;
295
- padding: 2px;
296
- text-align: center;
297
- font-weight: bold;
298
- }
299
  """
300
 
301
- # Button click event with JavaScript integration
302
- def process_with_js_color(input_image, haircut_style):
303
- """Process using color from JavaScript"""
304
- return change_haircut(input_image, haircut_style, selected_color.value)
 
 
 
 
 
 
 
 
 
 
 
305
 
 
306
  submit_btn.click(
307
- fn=process_with_js_color,
308
- inputs=[input_image, haircut_radio],
309
- outputs=[output_image, status_text, selected_color],
310
- js="() => { return [null, null, window.selectedHairColor || 'Black']; }"
311
- ).then(
312
  fn=change_haircut,
313
  inputs=[input_image, haircut_radio, selected_color],
314
- outputs=[output_image, status_text, selected_color]
 
 
 
 
 
 
 
315
  )
316
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
 
318
  # Run the app
319
  if __name__ == "__main__":
320
  # Check API key
321
  if not os.getenv("RAPI_TOKEN"):
322
- print("⚠️ Warning: REAPI_TOKEN environment variable not set!")
323
  print("Set it using: export RAPI_TOKEN='your_token'")
324
 
325
  demo.launch(
 
5
  import requests
6
  from io import BytesIO
7
 
8
+ # Replicate API client setup
9
+ # Set environment variable: export RAPI_TOKEN="your_token_here"
10
  replicate_client = replicate.Client(api_token=os.getenv("RAPI_TOKEN"))
11
 
12
+ # Comprehensive hairstyle options - All 93 styles
13
  HAIRCUT_OPTIONS = [
14
  "Crew Cut", "Faux Hawk", "Slicked Back", "Side-Parted", "Center-Parted",
15
  "Blunt Bangs", "Side-Swept Bangs", "Shag", "Lob", "Angled Bob",
 
65
  "Rose Gold": "#E0BFB8"
66
  }
67
 
68
+ def change_haircut(input_image, haircut_style, hair_color):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  """
70
+ Process image through RAPI to change hairstyle
71
  """
72
  try:
73
  # Check if image is provided
74
  if input_image is None:
75
+ return None, "Please upload an image first."
76
 
77
+ # Ensure we have a valid color
78
+ if not hair_color or hair_color not in HAIR_COLOR_OPTIONS:
79
+ hair_color = "Black"
80
 
81
  # Save PIL Image to temporary file
82
  temp_path = "temp_input.png"
 
106
  if os.path.exists(temp_path):
107
  os.remove(temp_path)
108
 
109
+ return result_image, f"Successfully applied {haircut_style} style with {hair_color} color!"
110
  else:
111
+ return None, "Processing error occurred."
112
 
113
  except Exception as e:
114
  # Clean up temporary file on error
115
  if os.path.exists(temp_path):
116
  os.remove(temp_path)
117
+ return None, f"Error: {str(e)}"
118
+
119
+ def update_color_display(color):
120
+ """Update the color display text"""
121
+ return f"**Selected Color:** {color}"
122
 
123
  # Create Gradio interface
124
  with gr.Blocks(title="AI Hairstyle Changer", theme=gr.themes.Soft()) as demo:
125
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
 
127
  with gr.Row():
128
  with gr.Column(scale=1):
 
136
  with gr.Group():
137
  gr.Markdown("### Select Hair Color")
138
  selected_color_display = gr.Markdown(value="**Selected Color:** Black")
139
+
140
+ # Create color buttons in a grid
141
+ color_buttons = []
142
+ with gr.Column():
143
+ # Create rows of color buttons
144
+ for i in range(0, len(HAIR_COLOR_OPTIONS), 5):
145
+ with gr.Row():
146
+ for color_name, color_code in list(HAIR_COLOR_OPTIONS.items())[i:i+5]:
147
+ btn = gr.Button(
148
+ value=color_name,
149
+ elem_classes="color-button",
150
+ elem_id=f"color-{color_name.replace(' ', '-')}"
151
+ )
152
+ color_buttons.append((btn, color_name, color_code))
153
+
154
+ # Hidden textbox to store selected color
155
+ selected_color = gr.Textbox(value="Black", visible=False)
156
 
157
  submit_btn = gr.Button(
158
  "🎨 Apply Hairstyle",
 
163
  with gr.Column(scale=2):
164
  # Hairstyle selection with scrollable radio buttons
165
  with gr.Group():
166
+ gr.Markdown(f"### Select Hairstyle (Total: {len(HAIRCUT_OPTIONS)} styles)")
167
  with gr.Column(elem_classes="hairstyle-container"):
168
  haircut_radio = gr.Radio(
169
  choices=HAIRCUT_OPTIONS,
170
  value="Bob",
171
  label="Hairstyle Options",
172
+ info="Browse and select from 93 hairstyle options",
173
  elem_classes="hairstyle-radio"
174
  )
175
 
 
187
  value="Upload a photo and select a hairstyle to begin."
188
  )
189
 
190
+ # Custom CSS with dynamic color styling
191
+ custom_css = """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  .hairstyle-container {
193
  max-height: 600px;
194
  overflow-y: auto;
 
211
  background-color: #e8e8e8;
212
  }
213
 
214
+ .color-button {
215
+ min-height: 50px !important;
216
+ margin: 2px !important;
217
+ border: 3px solid transparent !important;
218
+ border-radius: 8px !important;
219
+ font-weight: bold !important;
220
+ color: white !important;
221
+ text-shadow: 1px 1px 2px rgba(0,0,0,0.7) !important;
222
+ transition: all 0.3s ease !important;
223
+ position: relative !important;
224
  }
225
 
226
+ .color-button:hover {
227
+ transform: translateY(-2px) !important;
228
+ box-shadow: 0 4px 10px rgba(0,0,0,0.3) !important;
 
 
 
 
 
 
 
 
 
 
 
 
229
  }
230
 
231
+ .color-button.selected {
232
+ border-color: #2196F3 !important;
233
+ transform: scale(1.05) !important;
234
+ box-shadow: 0 0 0 3px rgba(33, 150, 243, 0.3) !important;
235
  }
236
+ """
237
 
238
+ # Add dynamic CSS for each color button
239
+ for color_name, color_code in HAIR_COLOR_OPTIONS.items():
240
+ custom_css += f"""
241
+ #color-{color_name.replace(' ', '-')} > button {{
242
+ background-color: {color_code} !important;
243
+ }}
 
 
 
 
 
 
244
  """
245
 
246
+ demo.css = custom_css
247
+
248
+ # Connect color button clicks
249
+ for btn, color_name, color_code in color_buttons:
250
+ btn.click(
251
+ fn=lambda c=color_name: (c, update_color_display(c)),
252
+ outputs=[selected_color, selected_color_display],
253
+ js=f"""(x) => {{
254
+ // Remove selected class from all buttons
255
+ document.querySelectorAll('.color-button').forEach(b => b.classList.remove('selected'));
256
+ // Add selected class to clicked button
257
+ document.querySelector('#color-{color_name.replace(' ', '-')} > button').classList.add('selected');
258
+ return '{color_name}';
259
+ }}"""
260
+ )
261
 
262
+ # Button click event
263
  submit_btn.click(
 
 
 
 
 
264
  fn=change_haircut,
265
  inputs=[input_image, haircut_radio, selected_color],
266
+ outputs=[output_image, status_text]
267
+ )
268
+
269
+ # Set initial selection
270
+ demo.load(
271
+ js="""() => {
272
+ document.querySelector('#color-Black > button').classList.add('selected');
273
+ }"""
274
  )
275
 
276
+ # Additional information
277
+ gr.Markdown(
278
+ """
279
+ ### 📚 Hairstyle Categories
280
+ - **Short Styles**: Pixie Cut, Crew Cut, Undercut, Mohawk
281
+ - **Medium Styles**: Bob, Lob, Shag, Layered
282
+ - **Long Styles**: Straight, Wavy, Curly
283
+ - **Braided Styles**: French Braid, Dutch Braid, Fishtail Braid, Box Braids
284
+ - **Updo Styles**: Chignon, French Twist, Messy Bun, Top Knot
285
+ - **Special Styles**: Space Buns, Dreadlocks, Cornrows, Beehive
286
+
287
+ ### 🎨 Hair Color Categories
288
+ - **Natural Colors**: Black, Browns, Blonde variations
289
+ - **Red Tones**: Red, Auburn, Copper, Mahogany, Burgundy
290
+ - **Cool Tones**: Silver, Ash Blonde, Ash Brown, Titanium
291
+ - **Fashion Colors**: Blue, Purple, Pink, Green, Rose Gold
292
+ """
293
+ )
294
 
295
  # Run the app
296
  if __name__ == "__main__":
297
  # Check API key
298
  if not os.getenv("RAPI_TOKEN"):
299
+ print("⚠️ Warning: RAPI_TOKEN environment variable not set!")
300
  print("Set it using: export RAPI_TOKEN='your_token'")
301
 
302
  demo.launch(