PareshEasel commited on
Commit
a381461
·
verified ·
1 Parent(s): 8121105

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +284 -0
app.py CHANGED
@@ -0,0 +1,284 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import requests
3
+ import os
4
+ import tempfile
5
+ import time
6
+ import json
7
+ from PIL import Image
8
+ import io
9
+ import base64
10
+ import urllib.request
11
+ from google.cloud import storage
12
+ from google.oauth2 import service_account
13
+ import json
14
+ import shutil
15
+
16
+ API_HOST = os.environ.get("FACESWAP_API_HOST")
17
+ API_PORT = os.environ.get("FACESWAP_API_PORT")
18
+ API_URL = f"http://{API_HOST}:{API_PORT}/faceswap"
19
+
20
+ def setup_gcs_auth():
21
+ """Set up GCS authentication using service account from environment variable"""
22
+ service_account_json = os.environ.get("SERVICE_ACCOUNT")
23
+ if service_account_json:
24
+ try:
25
+ service_account_info = json.loads(service_account_json)
26
+ credentials = service_account.Credentials.from_service_account_info(service_account_info)
27
+ return credentials
28
+ except Exception as e:
29
+ print(f"Error setting up GCS auth: {e}")
30
+ return None
31
+ return None
32
+
33
+ # Get GCS credentials
34
+ gcs_credentials = setup_gcs_auth()
35
+
36
+ def download_from_gcs(gcs_url):
37
+ """Download a file from GCS using authenticated client"""
38
+ try:
39
+ # Parse the GCS URL
40
+ if gcs_url.startswith("https://storage.googleapis.com/"):
41
+ path = gcs_url.replace("https://storage.googleapis.com/", "")
42
+ bucket_name, blob_path = path.split("/", 1)
43
+
44
+ storage_client = storage.Client(credentials=gcs_credentials) if gcs_credentials else storage.Client()
45
+ bucket = storage_client.bucket(bucket_name)
46
+ blob = bucket.blob(blob_path)
47
+
48
+ temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".gif")
49
+ blob.download_to_filename(temp_file.name)
50
+
51
+ return temp_file.name
52
+ else:
53
+ temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".gif")
54
+ urllib.request.urlretrieve(gcs_url, temp_file.name)
55
+ return temp_file.name
56
+ except Exception as e:
57
+ print(f"Error downloading from GCS: {e}")
58
+ return None
59
+
60
+ def faceswap_process(gif_file, face_file):
61
+ """Process faceswap by calling the API"""
62
+ if gif_file is None or face_file is None:
63
+ return None, "Please upload both a GIF and a face image."
64
+
65
+ temp_gif = tempfile.NamedTemporaryFile(suffix='.gif', delete=False)
66
+ temp_face = tempfile.NamedTemporaryFile(suffix='.jpg', delete=False)
67
+
68
+ try:
69
+ if hasattr(gif_file, 'name'):
70
+ if isinstance(gif_file, str):
71
+ shutil.copy(gif_file, temp_gif.name)
72
+ else:
73
+ with open(temp_gif.name, 'wb') as f:
74
+ f.write(gif_file.read())
75
+ else:
76
+ with open(temp_gif.name, 'wb') as f:
77
+ if hasattr(gif_file, 'read'):
78
+ f.write(gif_file.read())
79
+ elif isinstance(gif_file, str):
80
+ shutil.copy(gif_file, temp_gif.name)
81
+ else:
82
+ f.write(open(gif_file, 'rb').read())
83
+
84
+
85
+ if hasattr(face_file, 'name'):
86
+ if isinstance(face_file, str):
87
+ shutil.copy(face_file, temp_face.name)
88
+ else:
89
+ with open(temp_face.name, 'wb') as f:
90
+ f.write(face_file.read())
91
+ else:
92
+ with open(temp_face.name, 'wb') as f:
93
+ if hasattr(face_file, 'read'):
94
+ f.write(face_file.read())
95
+ elif isinstance(face_file, str):
96
+ shutil.copy(face_file, temp_face.name)
97
+ else:
98
+ f.write(open(face_file, 'rb').read())
99
+
100
+ files = {
101
+ 'gif_file': ('input.gif', open(temp_gif.name, 'rb'), 'image/gif'),
102
+ 'face_file': ('face.jpg', open(temp_face.name, 'rb'), 'image/jpeg')
103
+ }
104
+
105
+ start_time = time.time()
106
+ response = requests.post(API_URL, files=files)
107
+
108
+ # Clean up temporary files
109
+ os.unlink(temp_gif.name)
110
+ os.unlink(temp_face.name)
111
+
112
+ if response.status_code == 200:
113
+ result = response.json()
114
+
115
+ if result.get('status') == 'success':
116
+ output_url = result.get('url')
117
+ time_taken = result.get('time_taken', "Unknown")
118
+
119
+ # Download the GIF from GCS
120
+ local_gif_path = download_from_gcs(output_url)
121
+
122
+ if local_gif_path:
123
+ with open(local_gif_path, 'rb') as f:
124
+ gif_data = f.read()
125
+
126
+ gif_base64 = base64.b64encode(gif_data).decode('utf-8')
127
+
128
+ os.unlink(local_gif_path)
129
+
130
+ return f"data:image/gif;base64,{gif_base64}", f"✅ Faceswap completed in {time_taken}!"
131
+ else:
132
+ return None, "❌ Error downloading the result GIF"
133
+ else:
134
+ return None, f"❌ Error: {result.get('message', 'Unknown error')}"
135
+ else:
136
+ return None, f"❌ API Error: Status code {response.status_code}"
137
+
138
+ except Exception as e:
139
+ try:
140
+ os.unlink(temp_gif.name)
141
+ os.unlink(temp_face.name)
142
+ except:
143
+ pass
144
+
145
+ return None, f"❌ Error: {str(e)}"
146
+
147
+ # Custom CSS
148
+ custom_css = """
149
+ .centered-title {
150
+ text-align: center;
151
+ margin-bottom: 1.5rem;
152
+ }
153
+ .container {
154
+ max-width: 900px;
155
+ margin: 0 auto;
156
+ }
157
+ .output-container img {
158
+ max-width: 100%;
159
+ height: auto;
160
+ display: block;
161
+ margin: 0 auto;
162
+ }
163
+ /* Loading animation for output area only */
164
+ .loading-container {
165
+ text-align: center;
166
+ padding: 30px;
167
+ }
168
+ .loading-spinner {
169
+ display: inline-block;
170
+ width: 50px;
171
+ height: 50px;
172
+ border: 5px solid rgba(0, 0, 0, 0.1);
173
+ border-radius: 50%;
174
+ border-top-color: #3498db;
175
+ animation: spin 1s ease-in-out infinite;
176
+ }
177
+ @keyframes spin {
178
+ to { transform: rotate(360deg); }
179
+ }
180
+ .loading-text {
181
+ margin-top: 15px;
182
+ font-weight: bold;
183
+ color: #555;
184
+ }
185
+ """
186
+
187
+ with gr.Blocks(title="Easel x GIFFaceswap", css=custom_css) as demo:
188
+ with gr.Column(elem_classes="container"):
189
+ gr.HTML(
190
+ """
191
+ <div class="centered-title">
192
+ <h1>Easel x GIFFaceswap</h1>
193
+ <p>Upload a GIF and a face image to swap faces in the GIF.</p>
194
+ </div>
195
+ """
196
+ )
197
+
198
+ with gr.Row():
199
+ with gr.Column():
200
+ gif_input = gr.File(label="Upload GIF", file_types=[".gif"])
201
+ face_input = gr.File(label="Upload Face Image", file_types=[".jpg", ".jpeg", ".png"])
202
+ submit_btn = gr.Button("Swap Face", variant="primary")
203
+
204
+ with gr.Column(elem_classes="output-container"):
205
+ # Use HTML for displaying animated GIF with loading state
206
+ output_html = gr.HTML(label="Output GIF")
207
+ output_text = gr.Textbox(label="Status")
208
+
209
+ def process_and_display(gif_file, face_file):
210
+ if gif_file is None or face_file is None:
211
+ return (
212
+ "",
213
+ "Please upload both a GIF and a face image.",
214
+ gr.update(interactive=True)
215
+ )
216
+
217
+ # Perform the actual processing
218
+ base64_data, message = faceswap_process(gif_file, face_file)
219
+
220
+ if base64_data:
221
+ # Create HTML to display the animated GIF
222
+ html = f"""
223
+ <div style="text-align: center;">
224
+ <img src="{base64_data}" style="max-width:100%; height:auto;" alt="Faceswap Result" autoplay loop>
225
+ </div>
226
+ """
227
+ return (
228
+ html,
229
+ message,
230
+ gr.update(interactive=True)
231
+ )
232
+ else:
233
+ return (
234
+ "",
235
+ message,
236
+ gr.update(interactive=True)
237
+ )
238
+
239
+ # When the button is clicked, show loading in output area and disable button
240
+ def on_submit_click():
241
+ loading_html = """
242
+ <div class="loading-container">
243
+ <div class="loading-spinner"></div>
244
+ <div class="loading-text">Generating face-swapped GIF...</div>
245
+ </div>
246
+ """
247
+ return (
248
+ loading_html, # Show loading in output area
249
+ "Processing...", # Status text
250
+ gr.update(interactive=False) # Disable button
251
+ )
252
+
253
+ # Set up the click event with a two-step process
254
+ submit_btn.click(
255
+ fn=on_submit_click,
256
+ inputs=None,
257
+ outputs=[output_html, output_text, submit_btn]
258
+ ).then(
259
+ fn=process_and_display,
260
+ inputs=[gif_input, face_input],
261
+ outputs=[output_html, output_text, submit_btn]
262
+ )
263
+
264
+ # Enable/disable the submit button based on inputs
265
+ def check_inputs(gif, face):
266
+ if gif is not None and face is not None:
267
+ return gr.update(interactive=True)
268
+ return gr.update(interactive=False)
269
+
270
+ gif_input.change(
271
+ fn=check_inputs,
272
+ inputs=[gif_input, face_input],
273
+ outputs=[submit_btn]
274
+ )
275
+
276
+ face_input.change(
277
+ fn=check_inputs,
278
+ inputs=[gif_input, face_input],
279
+ outputs=[submit_btn]
280
+ )
281
+
282
+ # Launch the app
283
+ if __name__ == "__main__":
284
+ demo.launch(server_name="0.0.0.0", server_port=7860)