Upload app.py with huggingface_hub
Browse files
app.py
ADDED
@@ -0,0 +1,254 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import sys
|
3 |
+
import json
|
4 |
+
import time
|
5 |
+
import requests
|
6 |
+
import gradio as gr
|
7 |
+
import numpy as np
|
8 |
+
from PIL import Image
|
9 |
+
import io
|
10 |
+
import base64
|
11 |
+
import spaces
|
12 |
+
|
13 |
+
# ComfyUI API endpoint
|
14 |
+
COMFY_API = "http://127.0.0.1:8188/api"
|
15 |
+
WORKFLOW_PATH = "/app/workflows/Workflow_12_11.json"
|
16 |
+
|
17 |
+
# Load the workflow template
|
18 |
+
try:
|
19 |
+
with open(WORKFLOW_PATH, "r") as f:
|
20 |
+
workflow_template = json.load(f)
|
21 |
+
print(f"Loaded workflow template from {WORKFLOW_PATH}")
|
22 |
+
except Exception as e:
|
23 |
+
print(f"Error loading workflow template: {str(e)}")
|
24 |
+
workflow_template = {}
|
25 |
+
|
26 |
+
def queue_prompt(prompt):
|
27 |
+
"""Send a prompt to ComfyUI for processing"""
|
28 |
+
p = {"prompt": prompt}
|
29 |
+
try:
|
30 |
+
response = requests.post(f"{COMFY_API}/prompt", json=p)
|
31 |
+
return response.json()
|
32 |
+
except Exception as e:
|
33 |
+
print(f"Error queuing prompt: {str(e)}")
|
34 |
+
return {"error": str(e)}
|
35 |
+
|
36 |
+
def get_image(filename, subfolder, folder_type):
|
37 |
+
"""Get an image from ComfyUI's output folder"""
|
38 |
+
try:
|
39 |
+
response = requests.get(f"{COMFY_API}/view?filename={filename}&subfolder={subfolder}&type={folder_type}")
|
40 |
+
return Image.open(io.BytesIO(response.content))
|
41 |
+
except Exception as e:
|
42 |
+
print(f"Error getting image {filename}: {str(e)}")
|
43 |
+
return None
|
44 |
+
|
45 |
+
def upload_image(image, filename):
|
46 |
+
"""Upload an image to ComfyUI's input folder"""
|
47 |
+
try:
|
48 |
+
if isinstance(image, str): # Base64 string
|
49 |
+
image_data = base64.b64decode(image.split(",")[1])
|
50 |
+
files = {"image": (filename, image_data)}
|
51 |
+
else: # PIL Image or numpy array
|
52 |
+
if isinstance(image, np.ndarray):
|
53 |
+
image = Image.fromarray(image)
|
54 |
+
|
55 |
+
img_byte_arr = io.BytesIO()
|
56 |
+
image.save(img_byte_arr, format='PNG')
|
57 |
+
img_byte_arr.seek(0)
|
58 |
+
files = {"image": (filename, img_byte_arr.getvalue())}
|
59 |
+
|
60 |
+
response = requests.post(f"{COMFY_API}/upload/image", files=files)
|
61 |
+
return response.json()
|
62 |
+
except Exception as e:
|
63 |
+
print(f"Error uploading image: {str(e)}")
|
64 |
+
return {"error": str(e)}
|
65 |
+
|
66 |
+
def check_progress(prompt_id):
|
67 |
+
"""Check the progress of a ComfyUI prompt"""
|
68 |
+
try:
|
69 |
+
response = requests.get(f"{COMFY_API}/history/{prompt_id}")
|
70 |
+
return response.json()
|
71 |
+
except Exception as e:
|
72 |
+
print(f"Error checking progress: {str(e)}")
|
73 |
+
return {"error": str(e)}
|
74 |
+
|
75 |
+
@spaces.GPU
|
76 |
+
def generate_avatar(player_image, pose_image, shirt_image, player_name, team_name, age_gender_bg, shirt_color, style):
|
77 |
+
"""Generate a football player avatar using ComfyUI workflow"""
|
78 |
+
# Upload images to ComfyUI
|
79 |
+
player_upload = upload_image(player_image, "player.png")
|
80 |
+
pose_upload = upload_image(pose_image, "pose.png")
|
81 |
+
shirt_upload = upload_image(shirt_image, "shirt.png")
|
82 |
+
|
83 |
+
if "error" in player_upload or "error" in pose_upload or "error" in shirt_upload:
|
84 |
+
return None, f"Error uploading images: {player_upload.get('error', '')} {pose_upload.get('error', '')} {shirt_upload.get('error', '')}"
|
85 |
+
|
86 |
+
# Create a copy of the workflow template
|
87 |
+
workflow = workflow_template.copy()
|
88 |
+
|
89 |
+
# Update workflow nodes with our parameters
|
90 |
+
# Player image node
|
91 |
+
workflow["391"]["inputs"]["image"] = player_upload["name"]
|
92 |
+
|
93 |
+
# Pose image node
|
94 |
+
workflow["310"]["inputs"]["image"] = pose_upload["name"]
|
95 |
+
|
96 |
+
# Shirt image node
|
97 |
+
workflow["636"]["inputs"]["image"] = shirt_upload["name"]
|
98 |
+
|
99 |
+
# Player name node
|
100 |
+
workflow["471"]["inputs"]["string"] = f"_{player_name}"
|
101 |
+
|
102 |
+
# Team name node
|
103 |
+
workflow["667"]["inputs"]["string"] = team_name
|
104 |
+
|
105 |
+
# Age, gender, background prompt node
|
106 |
+
workflow["420"]["inputs"]["string"] = age_gender_bg
|
107 |
+
|
108 |
+
# Shirt color node
|
109 |
+
workflow["528"]["inputs"]["string"] = f"({shirt_color}:1.2) blank t-shirt, black shorts, "
|
110 |
+
|
111 |
+
# Style node
|
112 |
+
workflow["422"]["inputs"]["string"] = style
|
113 |
+
|
114 |
+
# Queue the prompt in ComfyUI
|
115 |
+
prompt_response = queue_prompt(workflow)
|
116 |
+
|
117 |
+
if "error" in prompt_response:
|
118 |
+
return None, f"Error queuing prompt: {prompt_response['error']}"
|
119 |
+
|
120 |
+
prompt_id = prompt_response["prompt_id"]
|
121 |
+
|
122 |
+
# Wait for the processing to complete
|
123 |
+
status = "Generating avatar..."
|
124 |
+
retries = 0
|
125 |
+
max_retries = 60 # 5 minutes timeout
|
126 |
+
|
127 |
+
while retries < max_retries:
|
128 |
+
time.sleep(5)
|
129 |
+
progress = check_progress(prompt_id)
|
130 |
+
|
131 |
+
if "error" in progress:
|
132 |
+
retries += 1
|
133 |
+
continue
|
134 |
+
|
135 |
+
if prompt_id in progress and len(progress[prompt_id]["outputs"]) > 0:
|
136 |
+
# Get the output image
|
137 |
+
for node_id, output in progress[prompt_id]["outputs"].items():
|
138 |
+
if node_id == "308" or node_id == "679": # Save Image nodes
|
139 |
+
image_filename = output.get("images", [{}])[0].get("filename", "")
|
140 |
+
if image_filename:
|
141 |
+
result_image = get_image(image_filename, "", "output")
|
142 |
+
masked_filename = output.get("images", [{}])[0].get("filename", "").replace(".png", "_Masked.png")
|
143 |
+
masked_image = get_image(masked_filename, "", "output")
|
144 |
+
|
145 |
+
# Return the masked image if available, otherwise the regular image
|
146 |
+
return masked_image if masked_image else result_image, "Avatar generated successfully!"
|
147 |
+
|
148 |
+
return None, "Completed, but couldn't find output image."
|
149 |
+
|
150 |
+
retries += 1
|
151 |
+
status = f"Generating avatar... (attempt {retries}/{max_retries})"
|
152 |
+
|
153 |
+
return None, "Timed out waiting for the avatar generation to complete."
|
154 |
+
|
155 |
+
def create_interface():
|
156 |
+
"""Create the Gradio interface for the avatar generator"""
|
157 |
+
with gr.Blocks(title="Football Player Avatar Generator") as demo:
|
158 |
+
gr.Markdown("# Football Player Avatar Generator")
|
159 |
+
gr.Markdown("Create stylized football player avatars from photos")
|
160 |
+
|
161 |
+
with gr.Row():
|
162 |
+
with gr.Column():
|
163 |
+
player_image = gr.Image(label="Upload Player Photo", type="pil")
|
164 |
+
|
165 |
+
with gr.Row():
|
166 |
+
pose_image = gr.Image(label="Select Pose Template", type="pil")
|
167 |
+
shirt_image = gr.Image(label="Select Shirt Template", type="pil")
|
168 |
+
|
169 |
+
player_name = gr.Textbox(label="Player Name", value="Player")
|
170 |
+
team_name = gr.Textbox(label="Team Name", value="Kurjet")
|
171 |
+
|
172 |
+
age_gender_options = [
|
173 |
+
"9 year old boy with on light grey studio background, upper body portrait",
|
174 |
+
"10 year old boy with on light grey studio background, upper body portrait",
|
175 |
+
"adult man with on light grey studio background, upper body portrait",
|
176 |
+
"adult woman with on light grey studio background, upper body portrait"
|
177 |
+
]
|
178 |
+
age_gender_bg = gr.Dropdown(label="Age, Gender & Background", choices=age_gender_options, value=age_gender_options[0])
|
179 |
+
|
180 |
+
shirt_color_options = ["black", "red", "blue", "green", "yellow", "white"]
|
181 |
+
shirt_color = gr.Dropdown(label="Shirt Color", choices=shirt_color_options, value="black")
|
182 |
+
|
183 |
+
style_options = [
|
184 |
+
"3d pixar character portrait, award winning, 3d animation, octane rendering",
|
185 |
+
"digital painting, detailed, concept art, smooth, sharp focus, illustration, trending on artstation",
|
186 |
+
"cartoon drawing, hand drawn, pencil on paper, sketch art",
|
187 |
+
"watercolor painting, beautiful, smooth, sharp focus, colorful, professional"
|
188 |
+
]
|
189 |
+
style = gr.Dropdown(label="Art Style", choices=style_options, value=style_options[0])
|
190 |
+
|
191 |
+
generate_button = gr.Button("Generate Avatar", variant="primary")
|
192 |
+
|
193 |
+
with gr.Column():
|
194 |
+
output_image = gr.Image(label="Generated Avatar")
|
195 |
+
status_text = gr.Textbox(label="Status", interactive=False)
|
196 |
+
|
197 |
+
# Load default images for pose and shirt
|
198 |
+
try:
|
199 |
+
default_pose = Image.open("/app/ComfyUI/input/pose4.jpg")
|
200 |
+
default_shirt = Image.open("/app/ComfyUI/input/paita2.jpg")
|
201 |
+
pose_image.value = default_pose
|
202 |
+
shirt_image.value = default_shirt
|
203 |
+
except Exception as e:
|
204 |
+
print(f"Error loading default images: {str(e)}")
|
205 |
+
|
206 |
+
# Set up the button click event
|
207 |
+
generate_button.click(
|
208 |
+
fn=generate_avatar,
|
209 |
+
inputs=[player_image, pose_image, shirt_image, player_name, team_name, age_gender_bg, shirt_color, style],
|
210 |
+
outputs=[output_image, status_text]
|
211 |
+
)
|
212 |
+
|
213 |
+
# Add examples
|
214 |
+
if os.path.exists("/app/ComfyUI/input"):
|
215 |
+
example_images = []
|
216 |
+
for filename in os.listdir("/app/ComfyUI/input"):
|
217 |
+
if filename.lower().endswith(('.png', '.jpg', '.jpeg')):
|
218 |
+
example_images.append(os.path.join("/app/ComfyUI/input", filename))
|
219 |
+
|
220 |
+
if example_images:
|
221 |
+
gr.Examples(
|
222 |
+
examples=[[img] for img in example_images[:3]],
|
223 |
+
inputs=[player_image]
|
224 |
+
)
|
225 |
+
|
226 |
+
return demo
|
227 |
+
|
228 |
+
# Check for ComfyUI server and launch the app
|
229 |
+
if __name__ == "__main__":
|
230 |
+
# Check if ComfyUI is running
|
231 |
+
retry_count = 0
|
232 |
+
max_retries = 5
|
233 |
+
comfy_running = False
|
234 |
+
|
235 |
+
while retry_count < max_retries and not comfy_running:
|
236 |
+
try:
|
237 |
+
response = requests.get(f"{COMFY_API}/system_stats")
|
238 |
+
if response.status_code == 200:
|
239 |
+
print("ComfyUI is running, starting Gradio interface...")
|
240 |
+
comfy_running = True
|
241 |
+
break
|
242 |
+
except:
|
243 |
+
pass
|
244 |
+
|
245 |
+
retry_count += 1
|
246 |
+
print(f"Waiting for ComfyUI to start... (attempt {retry_count}/{max_retries})")
|
247 |
+
time.sleep(10)
|
248 |
+
|
249 |
+
if not comfy_running:
|
250 |
+
print("WARNING: Could not connect to ComfyUI server. The application may not function correctly.")
|
251 |
+
|
252 |
+
# Create and launch the interface
|
253 |
+
demo = create_interface()
|
254 |
+
demo.launch(server_name="0.0.0.0", server_port=7860, share=False)
|