nalin0503 commited on
Commit
caa06b8
·
1 Parent(s): 9d2aab3

Create 3 page sys, lastt

Browse files
Files changed (1) hide show
  1. app.py +337 -311
app.py CHANGED
@@ -46,12 +46,9 @@ def create_temp_folder():
46
  return run_folder
47
 
48
  def main():
49
- # Initialize session state for tracking processing status
50
- if 'processing' not in st.session_state:
51
- st.session_state.processing = False
52
-
53
- if 'process_complete' not in st.session_state:
54
- st.session_state.process_complete = False
55
 
56
  if 'temp_dir' not in st.session_state:
57
  st.session_state.temp_dir = None
@@ -59,18 +56,20 @@ def main():
59
  if 'final_video_path' not in st.session_state:
60
  st.session_state.final_video_path = None
61
 
62
- # Initialize the download container in session state
63
- if 'show_download' not in st.session_state:
64
- st.session_state.show_download = False
 
 
 
 
65
 
66
- # Restart function to reset session state
67
- def restart():
68
- st.session_state.processing = False
69
- st.session_state.process_complete = False
70
  st.session_state.temp_dir = None
71
  st.session_state.final_video_path = None
72
- st.session_state.show_download = False
73
- st.rerun() # Use st.rerun() instead of experimental_rerun
74
 
75
  # ---------------- CUSTOM CSS FOR A PROFESSIONAL, DARK THEME ----------------
76
  st.markdown(
@@ -144,31 +143,20 @@ def main():
144
  transform: scale(1.02);
145
  box-shadow: 0 0 20px rgba(142,68,173,0.8), 0 0 30px rgba(114,45,145,0.6);
146
  }
147
- /* Disabled button styling */
148
- div.stButton > button:disabled {
149
- background-image: linear-gradient(45deg, #6b6b6b, #4a4a4a);
150
- box-shadow: none;
151
- cursor: not-allowed;
152
- opacity: 0.7;
153
- }
154
- /* File uploader label styling */
155
- .stFileUploader label {
156
- font-size: 1rem;
157
- color: #f1f1f1;
158
  }
159
- /* Advanced Options divider styling */
160
- .right-column-divider {
161
- border-left: 2px solid #f1f1f1;
162
- padding-left: 1rem;
163
- margin-left: 1rem;
164
  }
165
- /* Results section styling */
166
- .results-section {
167
- margin-top: 2rem;
168
- padding: 1.5rem;
169
- background-color: rgba(0, 0, 0, 0.2);
170
- border-radius: 8px;
171
- border: 1px solid rgba(255, 255, 255, 0.1);
172
  }
173
  </style>
174
  """,
@@ -182,7 +170,7 @@ def main():
182
  st.error("Please make sure all required scripts are in the same directory as this Streamlit app.")
183
  return
184
 
185
- # ---------------- HEADER & LOGO ----------------
186
  logo_path = "metamorphLogo_nobg.png"
187
  if os.path.exists(logo_path):
188
  try:
@@ -200,302 +188,340 @@ def main():
200
  st.warning(f"Logo could not be loaded: {e}")
201
 
202
  st.markdown("<h1 class='header-title'>Metamorph Web App</h1>", unsafe_allow_html=True)
203
- st.markdown(
204
- """
205
- <p style='text-align: center; font-size: 1.1rem;'>
206
- DiffMorpher is used for keyframe generation by default, with FILM for interpolation.
207
- Optionally, you can enable LCM-LoRA for accelerated inference (with slight decrease in quality).
208
- Upload two images, optionally provide textual prompts, and fine-tune the settings to create a smooth, high-quality morphing video.
209
- </p>
210
- <hr>
211
- """,
212
- unsafe_allow_html=True
213
- )
214
 
215
- # Create a persistent container for the results section
216
- results_container = st.container()
 
 
 
 
 
 
 
 
 
 
 
217
 
218
- # Show results if processing is complete
219
- if st.session_state.process_complete and st.session_state.final_video_path is not None:
220
- with results_container:
221
- st.markdown("<div class='results-section'>", unsafe_allow_html=True)
222
- st.success("Morphing complete! 🎉")
223
- try:
224
- with open(st.session_state.final_video_path, "rb") as f:
225
- video_bytes = f.read()
226
- st.session_state.video_bytes = video_bytes # Store video bytes in session state
227
- st.video(video_bytes)
228
- st.download_button(
229
- "Download Morphing Video",
230
- data=video_bytes,
231
- file_name="metamorph_result.mp4",
232
- mime="video/mp4"
233
- )
234
- st.session_state.show_download = True
235
-
236
- # Add a restart button to clear the session state and start over
237
- if st.button("Start New Morphing Project", key="restart"):
238
- restart()
239
-
240
- except Exception as e:
241
- st.error(f"Error preparing video for download: {e}")
242
- st.markdown("</div>", unsafe_allow_html=True)
243
 
244
- # ---------------- SECTION 1: IMAGE & PROMPT INPUTS ----------------
245
- st.subheader("1. Upload Source Images & Prompts")
246
- st.markdown("**Note:** Your uploaded images must be of similar topology and same size to achieve the best results.")
247
- col_imgA, col_imgB = st.columns(2)
248
- with col_imgA:
249
- st.markdown("#### Image A")
250
- uploaded_image_A = st.file_uploader("Upload your first image", type=["png", "jpg", "jpeg"], key="imgA",
251
- disabled=st.session_state.processing)
252
- if uploaded_image_A is not None:
253
- st.image(uploaded_image_A, caption="Preview - Image A", use_container_width=True)
254
- prompt_A = st.text_input("Short Description for Image A (optional)", value="", key="promptA",
255
- help="For added interpolation between the two descriptions",
256
- disabled=st.session_state.processing)
257
- with col_imgB:
258
- st.markdown("#### Image B")
259
- uploaded_image_B = st.file_uploader("Upload your second image", type=["png", "jpg", "jpeg"], key="imgB",
260
- disabled=st.session_state.processing)
261
- if uploaded_image_B is not None:
262
- st.image(uploaded_image_B, caption="Preview - Image B", use_container_width=True)
263
- prompt_B = st.text_input("Short Description for Image B (optional)", value="", key="promptB",
264
- help="For added interpolation between the two descriptions",
265
- disabled=st.session_state.processing)
266
 
267
- st.markdown("<hr>", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
268
 
269
- # ---------------- SECTION 2: CONFIGURE MORPHING PIPELINE ----------------
270
- st.subheader("2. Configure Morphing Pipeline")
271
- st.markdown(
272
- """
273
- <p style="font-size: 1rem;">
274
- Select a preset below to automatically adjust quality and inference time.
275
- If you choose <strong>Custom ⚙️</strong>, the advanced settings will automatically expand so you can fine-tune the configuration.
276
- </p>
277
- """,
278
- unsafe_allow_html=True
279
- )
 
 
 
280
 
281
- # Preset Options (Dropdown)
282
- st.markdown("**Preset Options**")
283
- preset_option = st.selectbox(
284
- "Select a preset for quality and inference time",
285
- options=[
286
- "Maximum quality, longest inference time 🏆",
287
- "Medium quality, medium inference time ⚖️",
288
- "Low quality, shortest inference time ⚡",
289
- "Creative morph 🎨",
290
- "Custom ⚙️"
291
- ],
292
- index=0,
293
- label_visibility="collapsed",
294
- disabled=st.session_state.processing
295
- )
 
 
 
 
 
 
 
296
 
297
- # Determine preset defaults based on selection
298
- if preset_option.startswith("Maximum quality"):
299
- preset_model = "Base Stable Diffusion V2-1"
300
- preset_film = True
301
- preset_lcm = False
302
- elif preset_option.startswith("Medium quality"):
303
- preset_model = "Base Stable Diffusion V2-1"
304
- preset_film = False
305
- preset_lcm = False
306
- elif preset_option.startswith("Low quality"):
307
- preset_model = "Base Stable Diffusion V1-5"
308
- preset_film = False
309
- preset_lcm = True
310
- elif preset_option.startswith("Creative morph"):
311
- preset_model = "Dreamshaper-7 (fine-tuned SD V1-5)"
312
- preset_film = True
313
- preset_lcm = True
314
- else:
315
- # "Custom"
316
- preset_model = None
317
- preset_film = None
318
- preset_lcm = None
319
 
320
- advanced_expanded = True if preset_option.endswith("⚙️") else False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
 
322
- # Advanced Options for fine-tuning
323
- with st.expander("Advanced Options", expanded=advanced_expanded):
324
- options_list = [
325
- "Base Stable Diffusion V1-5",
326
- "Dreamshaper-7 (fine-tuned SD V1-5)",
327
- "Base Stable Diffusion V2-1"
328
- ]
329
- default_model = preset_model if preset_model is not None else "Base Stable Diffusion V1-5"
330
- default_index = options_list.index(default_model)
331
- model_option = st.selectbox("Select Model Card", options=options_list, index=default_index, disabled=st.session_state.processing)
332
-
333
- col_left, col_right = st.columns(2)
334
- # Left Column: Keyframe Generator Parameters
335
- with col_left:
336
- st.markdown("##### Keyframe Generator Parameters")
337
- num_frames = st.number_input("Number of keyframes (2–50)", min_value=2, max_value=50, value=16, disabled=st.session_state.processing)
338
- lcm_default = preset_lcm if preset_lcm is not None else False
339
- enable_lcm_lora = st.checkbox(
340
- "Enable LCM-LoRA",
341
- value=lcm_default,
342
- help="Accelerates inference with slight quality decrease",
343
- disabled=st.session_state.processing
344
- )
345
- use_adain = st.checkbox("Use AdaIN", value=True, help="Adaptive Instance Normalization for improved generation", disabled=st.session_state.processing)
346
- use_reschedule = st.checkbox("Use reschedule sampling", value=True, help="Better sampling strategy", disabled=st.session_state.processing)
347
-
348
- # Right Column: Inter-frame Interpolator Parameters (FILM)
349
- with col_right:
350
- st.markdown("<div class='right-column-divider'>", unsafe_allow_html=True)
351
- st.markdown("##### Inter-frame Interpolator Parameters")
352
- default_use_film = preset_film if preset_film is not None else True
353
- use_film = st.checkbox("Use FILM interpolation", value=default_use_film, help="Frame Interpolation for Large Motion - creates smooth transitions", disabled=st.session_state.processing)
354
- film_recursions = st.number_input("FILM recursion passes (1–6)", min_value=1, max_value=6, value=3,
355
- help="Higher values create more intermediate frames (smoother but slower)",
356
- disabled=st.session_state.processing)
357
- # Set default FPS based on whether FILM is enabled
358
- default_fps = 30 if use_film else 8
359
- output_fps = st.number_input("Output FPS (1–120)", min_value=1, max_value=120, value=default_fps,
360
- help="Output video frames per second",
361
- disabled=st.session_state.processing)
362
- st.markdown("</div>", unsafe_allow_html=True)
363
 
364
- st.markdown("<hr>", unsafe_allow_html=True)
 
 
365
 
366
- # ---------------- SECTION 3: EXECUTE MORPH PIPELINE ----------------
367
- st.subheader("3. Generate Morphing Video")
368
- st.markdown("Once satisfied with your inputs, click below to start the process.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
369
 
370
- # Create a container for the run button and its status messages
371
- run_container = st.container()
372
-
373
- # Create a separate container for displaying the download button
374
- download_container = st.container()
375
-
376
- # Check if we should display the download button even when the form is disabled
377
- if st.session_state.show_download and st.session_state.final_video_path is not None:
378
- with download_container:
379
- st.markdown("<div class='results-section'>", unsafe_allow_html=True)
380
- st.success("Morphing complete! 🎉")
 
 
 
 
 
 
 
 
 
 
 
 
381
  try:
382
- with open(st.session_state.final_video_path, "rb") as f:
383
- video_bytes = f.read()
384
- st.video(video_bytes)
385
- st.download_button(
386
- "Download Morphing Video",
387
- data=video_bytes,
388
- file_name="metamorph_result.mp4",
389
- mime="video/mp4"
390
- )
391
 
392
- # Add a restart button to clear the session state and start over
393
- if st.button("Start New Morphing Project", key="restart_bottom"):
394
- restart()
395
-
396
- except Exception as e:
397
- st.error(f"Error preparing video for download: {e}")
398
- st.markdown("</div>", unsafe_allow_html=True)
399
-
400
- with run_container:
401
- # Button text changes based on processing state
402
- button_text = "Processing... Please Wait" if st.session_state.processing else "Run Morphing Pipeline"
403
-
404
- # Button is disabled during processing
405
- if st.button(button_text, key="run_pipeline", disabled=st.session_state.processing):
406
- if not (uploaded_image_A and uploaded_image_B):
407
- st.error("Please upload both images before running the morphing pipeline.")
408
- else:
409
- # Set the processing state to True to prevent multiple runs
410
- st.session_state.processing = True
411
- st.info("Processing your morphing request. Please wait...")
 
 
412
 
413
- # Create a temporary folder for processing
414
- temp_dir = create_temp_folder()
415
- st.session_state.temp_dir = temp_dir
 
 
 
416
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
417
  try:
418
- # Save uploaded images
419
- imgA_path = os.path.join(temp_dir, "imageA.png")
420
- imgB_path = os.path.join(temp_dir, "imageB.png")
421
- save_uploaded_file(uploaded_image_A, imgA_path)
422
- save_uploaded_file(uploaded_image_B, imgB_path)
423
-
424
- # Create output directories
425
- timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
426
- output_dir = os.path.join(temp_dir, f"morph_results_{timestamp}")
427
- film_output_dir = os.path.join(temp_dir, f"film_output_{timestamp}")
428
- os.makedirs(output_dir, exist_ok=True)
429
- os.makedirs(film_output_dir, exist_ok=True)
430
-
431
- actual_model_path = (
432
- "lykon/dreamshaper-7" if model_option == "Dreamshaper-7 (fine-tuned SD V1-5)"
433
- else "stabilityai/stable-diffusion-2-1-base" if model_option == "Base Stable Diffusion V2-1"
434
- else "sd-legacy/stable-diffusion-v1-5"
435
- )
436
 
437
- # Build the command for run_morphing.py
438
- cmd = [
439
- sys.executable, "run_morphing.py",
440
- "--model_path", actual_model_path,
441
- "--image_path_0", imgA_path,
442
- "--image_path_1", imgB_path,
443
- "--prompt_0", prompt_A,
444
- "--prompt_1", prompt_B,
445
- "--output_path", output_dir,
446
- "--film_output_folder", film_output_dir,
447
- "--num_frames", str(num_frames),
448
- "--fps", str(output_fps)
449
- ]
450
 
451
- if enable_lcm_lora:
452
- cmd.append("--use_lcm")
453
- if use_adain:
454
- cmd.append("--use_adain")
455
- if use_reschedule:
456
- cmd.append("--use_reschedule")
457
- if use_film:
458
- cmd.append("--use_film")
459
 
460
- # Add film recursion parameter
461
- cmd.extend(["--film_num_recursions", str(film_recursions)])
 
 
 
 
462
 
463
- # Run the morphing process
464
- try:
465
- subprocess.run(cmd, check=True)
466
-
467
- # Check for output video
468
- video_found = False
469
- possible_outputs = [f for f in os.listdir(film_output_dir) if f.endswith(".mp4")]
470
  if possible_outputs:
471
- final_video_path = os.path.join(film_output_dir, possible_outputs[0])
472
  video_found = True
473
-
474
- if not video_found:
475
- possible_outputs = [f for f in os.listdir(output_dir) if f.endswith(".mp4")]
476
- if possible_outputs:
477
- final_video_path = os.path.join(output_dir, possible_outputs[0])
478
- video_found = True
479
-
480
- if video_found:
481
- st.session_state.final_video_path = final_video_path
482
- st.session_state.process_complete = True
483
- st.session_state.show_download = True
484
- st.rerun() # This is crucial for showing the results page
485
- else:
486
- st.error("No output video was generated. Check logs for details.")
487
- st.session_state.processing = False
488
- st.session_state.process_complete = False
489
-
490
- except subprocess.CalledProcessError as e:
491
- st.error(f"Error running morphing pipeline: {e}")
492
- st.session_state.processing = False
493
- st.session_state.process_complete = False
494
 
495
- except Exception as e:
496
- st.error(f"An error occurred during processing: {e}")
497
- st.session_state.processing = False
498
- st.session_state.process_complete = False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
499
 
500
  if __name__ == "__main__":
501
  main()
 
46
  return run_folder
47
 
48
  def main():
49
+ # Initialize session state variables
50
+ if 'page' not in st.session_state:
51
+ st.session_state.page = 'input' # States: 'input', 'processing', 'result'
 
 
 
52
 
53
  if 'temp_dir' not in st.session_state:
54
  st.session_state.temp_dir = None
 
56
  if 'final_video_path' not in st.session_state:
57
  st.session_state.final_video_path = None
58
 
59
+ if 'process_started' not in st.session_state:
60
+ st.session_state.process_started = False
61
+
62
+ # Function to switch to processing page and start morphing
63
+ def start_processing():
64
+ st.session_state.page = 'processing'
65
+ st.session_state.process_started = False # Will be set to True when processing starts
66
 
67
+ # Function to return to input page
68
+ def return_to_input():
69
+ st.session_state.page = 'input'
 
70
  st.session_state.temp_dir = None
71
  st.session_state.final_video_path = None
72
+ st.session_state.process_started = False
 
73
 
74
  # ---------------- CUSTOM CSS FOR A PROFESSIONAL, DARK THEME ----------------
75
  st.markdown(
 
143
  transform: scale(1.02);
144
  box-shadow: 0 0 20px rgba(142,68,173,0.8), 0 0 30px rgba(114,45,145,0.6);
145
  }
146
+ /* Processing animation */
147
+ .processing-container {
148
+ text-align: center;
149
+ padding: 3rem 0;
 
 
 
 
 
 
 
150
  }
151
+ .processing-text {
152
+ font-size: 1.8rem;
153
+ margin-bottom: 2rem;
154
+ animation: pulse 2s infinite;
 
155
  }
156
+ @keyframes pulse {
157
+ 0% { opacity: 0.6; }
158
+ 50% { opacity: 1; }
159
+ 100% { opacity: 0.6; }
 
 
 
160
  }
161
  </style>
162
  """,
 
170
  st.error("Please make sure all required scripts are in the same directory as this Streamlit app.")
171
  return
172
 
173
+ # Load logo for all pages
174
  logo_path = "metamorphLogo_nobg.png"
175
  if os.path.exists(logo_path):
176
  try:
 
188
  st.warning(f"Logo could not be loaded: {e}")
189
 
190
  st.markdown("<h1 class='header-title'>Metamorph Web App</h1>", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
191
 
192
+ # =============== INPUT PAGE ===============
193
+ if st.session_state.page == 'input':
194
+ st.markdown(
195
+ """
196
+ <p style='text-align: center; font-size: 1.1rem;'>
197
+ DiffMorpher is used for keyframe generation by default, with FILM for interpolation.
198
+ Optionally, you can enable LCM-LoRA for accelerated inference (with slight decrease in quality).
199
+ Upload two images, optionally provide textual prompts, and fine-tune the settings to create a smooth, high-quality morphing video.
200
+ </p>
201
+ <hr>
202
+ """,
203
+ unsafe_allow_html=True
204
+ )
205
 
206
+ # ---------------- SECTION 1: IMAGE & PROMPT INPUTS ----------------
207
+ st.subheader("1. Upload Source Images & Prompts")
208
+ st.markdown("**Note:** Your uploaded images must be of similar topology and same size to achieve the best results.")
209
+ col_imgA, col_imgB = st.columns(2)
210
+ with col_imgA:
211
+ st.markdown("#### Image A")
212
+ uploaded_image_A = st.file_uploader("Upload your first image", type=["png", "jpg", "jpeg"], key="imgA")
213
+ if uploaded_image_A is not None:
214
+ st.image(uploaded_image_A, caption="Preview - Image A", use_container_width=True)
215
+ prompt_A = st.text_input("Short Description for Image A (optional)", value="", key="promptA",
216
+ help="For added interpolation between the two descriptions")
217
+ with col_imgB:
218
+ st.markdown("#### Image B")
219
+ uploaded_image_B = st.file_uploader("Upload your second image", type=["png", "jpg", "jpeg"], key="imgB")
220
+ if uploaded_image_B is not None:
221
+ st.image(uploaded_image_B, caption="Preview - Image B", use_container_width=True)
222
+ prompt_B = st.text_input("Short Description for Image B (optional)", value="", key="promptB",
223
+ help="For added interpolation between the two descriptions")
 
 
 
 
 
 
 
224
 
225
+ st.markdown("<hr>", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
 
227
+ # ---------------- SECTION 2: CONFIGURE MORPHING PIPELINE ----------------
228
+ st.subheader("2. Configure Morphing Pipeline")
229
+ st.markdown(
230
+ """
231
+ <p style="font-size: 1rem;">
232
+ Select a preset below to automatically adjust quality and inference time.
233
+ If you choose <strong>Custom ⚙️</strong>, the advanced settings will automatically expand so you can fine-tune the configuration.
234
+ </p>
235
+ """,
236
+ unsafe_allow_html=True
237
+ )
238
 
239
+ # Preset Options (Dropdown)
240
+ st.markdown("**Preset Options**")
241
+ preset_option = st.selectbox(
242
+ "Select a preset for quality and inference time",
243
+ options=[
244
+ "Maximum quality, longest inference time 🏆",
245
+ "Medium quality, medium inference time ⚖️",
246
+ "Low quality, shortest inference time ⚡",
247
+ "Creative morph 🎨",
248
+ "Custom ⚙️"
249
+ ],
250
+ index=0,
251
+ label_visibility="collapsed"
252
+ )
253
 
254
+ # Determine preset defaults based on selection
255
+ if preset_option.startswith("Maximum quality"):
256
+ preset_model = "Base Stable Diffusion V2-1"
257
+ preset_film = True
258
+ preset_lcm = False
259
+ elif preset_option.startswith("Medium quality"):
260
+ preset_model = "Base Stable Diffusion V2-1"
261
+ preset_film = False
262
+ preset_lcm = False
263
+ elif preset_option.startswith("Low quality"):
264
+ preset_model = "Base Stable Diffusion V1-5"
265
+ preset_film = False
266
+ preset_lcm = True
267
+ elif preset_option.startswith("Creative morph"):
268
+ preset_model = "Dreamshaper-7 (fine-tuned SD V1-5)"
269
+ preset_film = True
270
+ preset_lcm = True
271
+ else:
272
+ # "Custom"
273
+ preset_model = None
274
+ preset_film = None
275
+ preset_lcm = None
276
 
277
+ advanced_expanded = True if preset_option.endswith("⚙️") else False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
 
279
+ # Advanced Options for fine-tuning
280
+ with st.expander("Advanced Options", expanded=advanced_expanded):
281
+ options_list = [
282
+ "Base Stable Diffusion V1-5",
283
+ "Dreamshaper-7 (fine-tuned SD V1-5)",
284
+ "Base Stable Diffusion V2-1"
285
+ ]
286
+ default_model = preset_model if preset_model is not None else "Base Stable Diffusion V1-5"
287
+ default_index = options_list.index(default_model)
288
+ model_option = st.selectbox("Select Model Card", options=options_list, index=default_index)
289
+
290
+ col_left, col_right = st.columns(2)
291
+ # Left Column: Keyframe Generator Parameters
292
+ with col_left:
293
+ st.markdown("##### Keyframe Generator Parameters")
294
+ num_frames = st.number_input("Number of keyframes (2–50)", min_value=2, max_value=50, value=16)
295
+ lcm_default = preset_lcm if preset_lcm is not None else False
296
+ enable_lcm_lora = st.checkbox(
297
+ "Enable LCM-LoRA",
298
+ value=lcm_default,
299
+ help="Accelerates inference with slight quality decrease"
300
+ )
301
+ use_adain = st.checkbox("Use AdaIN", value=True, help="Adaptive Instance Normalization for improved generation")
302
+ use_reschedule = st.checkbox("Use reschedule sampling", value=True, help="Better sampling strategy")
303
+
304
+ # Right Column: Inter-frame Interpolator Parameters (FILM)
305
+ with col_right:
306
+ st.markdown("<div class='right-column-divider'>", unsafe_allow_html=True)
307
+ st.markdown("##### Inter-frame Interpolator Parameters")
308
+ default_use_film = preset_film if preset_film is not None else True
309
+ use_film = st.checkbox("Use FILM interpolation", value=default_use_film, help="Frame Interpolation for Large Motion - creates smooth transitions")
310
+ film_recursions = st.number_input("FILM recursion passes (1–6)", min_value=1, max_value=6, value=3,
311
+ help="Higher values create more intermediate frames (smoother but slower)")
312
+ # Set default FPS based on whether FILM is enabled
313
+ default_fps = 30 if use_film else 8
314
+ output_fps = st.number_input("Output FPS (1–120)", min_value=1, max_value=120, value=default_fps,
315
+ help="Output video frames per second")
316
+ st.markdown("</div>", unsafe_allow_html=True)
317
 
318
+ st.markdown("<hr>", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
319
 
320
+ # ---------------- SECTION 3: EXECUTE MORPH PIPELINE ----------------
321
+ st.subheader("3. Generate Morphing Video")
322
+ st.markdown("Once satisfied with your inputs, click below to start the process.")
323
 
324
+ # Create a container for the run button
325
+ run_container = st.container()
326
+
327
+ with run_container:
328
+ # Save values to session state so we can access them in the processing page
329
+ if st.button("Run Morphing Pipeline", key="run_pipeline"):
330
+ if not (uploaded_image_A and uploaded_image_B):
331
+ st.error("Please upload both images before running the morphing pipeline.")
332
+ else:
333
+ # Save all settings to session state
334
+ st.session_state.uploaded_image_A = uploaded_image_A
335
+ st.session_state.uploaded_image_B = uploaded_image_B
336
+ st.session_state.prompt_A = prompt_A
337
+ st.session_state.prompt_B = prompt_B
338
+ st.session_state.model_option = model_option
339
+ st.session_state.num_frames = num_frames
340
+ st.session_state.enable_lcm_lora = enable_lcm_lora
341
+ st.session_state.use_adain = use_adain
342
+ st.session_state.use_reschedule = use_reschedule
343
+ st.session_state.use_film = use_film
344
+ st.session_state.film_recursions = film_recursions
345
+ st.session_state.output_fps = output_fps
346
+
347
+ # Switch to processing page
348
+ start_processing()
349
+ st.rerun()
350
 
351
+ # =============== PROCESSING PAGE ===============
352
+ elif st.session_state.page == 'processing':
353
+ st.markdown(
354
+ """
355
+ <div class="processing-container">
356
+ <h2 class="processing-text">Processing Your Morphing Request</h2>
357
+ <p>Please wait while we generate your morphing video...</p>
358
+ </div>
359
+ """,
360
+ unsafe_allow_html=True
361
+ )
362
+
363
+ # Use a progress bar for visual feedback
364
+ progress_bar = st.progress(0)
365
+
366
+ # Only start processing if not already started
367
+ if not st.session_state.process_started:
368
+ st.session_state.process_started = True
369
+
370
+ # Create a temporary folder for processing
371
+ temp_dir = create_temp_folder()
372
+ st.session_state.temp_dir = temp_dir
373
+
374
  try:
375
+ # Update progress
376
+ progress_bar.progress(10)
 
 
 
 
 
 
 
377
 
378
+ # Extract variables from session state
379
+ uploaded_image_A = st.session_state.uploaded_image_A
380
+ uploaded_image_B = st.session_state.uploaded_image_B
381
+ prompt_A = st.session_state.prompt_A
382
+ prompt_B = st.session_state.prompt_B
383
+ model_option = st.session_state.model_option
384
+ num_frames = st.session_state.num_frames
385
+ enable_lcm_lora = st.session_state.enable_lcm_lora
386
+ use_adain = st.session_state.use_adain
387
+ use_reschedule = st.session_state.use_reschedule
388
+ use_film = st.session_state.use_film
389
+ film_recursions = st.session_state.film_recursions
390
+ output_fps = st.session_state.output_fps
391
+
392
+ # Save uploaded images
393
+ imgA_path = os.path.join(temp_dir, "imageA.png")
394
+ imgB_path = os.path.join(temp_dir, "imageB.png")
395
+ save_uploaded_file(uploaded_image_A, imgA_path)
396
+ save_uploaded_file(uploaded_image_B, imgB_path)
397
+
398
+ # Update progress
399
+ progress_bar.progress(20)
400
 
401
+ # Create output directories
402
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
403
+ output_dir = os.path.join(temp_dir, f"morph_results_{timestamp}")
404
+ film_output_dir = os.path.join(temp_dir, f"film_output_{timestamp}")
405
+ os.makedirs(output_dir, exist_ok=True)
406
+ os.makedirs(film_output_dir, exist_ok=True)
407
 
408
+ actual_model_path = (
409
+ "lykon/dreamshaper-7" if model_option == "Dreamshaper-7 (fine-tuned SD V1-5)"
410
+ else "stabilityai/stable-diffusion-2-1-base" if model_option == "Base Stable Diffusion V2-1"
411
+ else "sd-legacy/stable-diffusion-v1-5"
412
+ )
413
+
414
+ # Update progress
415
+ progress_bar.progress(30)
416
+
417
+ # Build the command for run_morphing.py
418
+ cmd = [
419
+ sys.executable, "run_morphing.py",
420
+ "--model_path", actual_model_path,
421
+ "--image_path_0", imgA_path,
422
+ "--image_path_1", imgB_path,
423
+ "--prompt_0", prompt_A,
424
+ "--prompt_1", prompt_B,
425
+ "--output_path", output_dir,
426
+ "--film_output_folder", film_output_dir,
427
+ "--num_frames", str(num_frames),
428
+ "--fps", str(output_fps)
429
+ ]
430
+
431
+ if enable_lcm_lora:
432
+ cmd.append("--use_lcm")
433
+ if use_adain:
434
+ cmd.append("--use_adain")
435
+ if use_reschedule:
436
+ cmd.append("--use_reschedule")
437
+ if use_film:
438
+ cmd.append("--use_film")
439
+
440
+ # Add film recursion parameter
441
+ cmd.extend(["--film_num_recursions", str(film_recursions)])
442
+
443
+ # Run the morphing process
444
  try:
445
+ # Update progress - processing takes the longest
446
+ progress_bar.progress(40)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
447
 
448
+ subprocess.run(cmd, check=True)
 
 
 
 
 
 
 
 
 
 
 
 
449
 
450
+ # Update progress
451
+ progress_bar.progress(90)
 
 
 
 
 
 
452
 
453
+ # Check for output video
454
+ video_found = False
455
+ possible_outputs = [f for f in os.listdir(film_output_dir) if f.endswith(".mp4")]
456
+ if possible_outputs:
457
+ final_video_path = os.path.join(film_output_dir, possible_outputs[0])
458
+ video_found = True
459
 
460
+ if not video_found:
461
+ possible_outputs = [f for f in os.listdir(output_dir) if f.endswith(".mp4")]
 
 
 
 
 
462
  if possible_outputs:
463
+ final_video_path = os.path.join(output_dir, possible_outputs[0])
464
  video_found = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
465
 
466
+ if video_found:
467
+ st.session_state.final_video_path = final_video_path
468
+ st.session_state.page = 'result'
469
+ progress_bar.progress(100)
470
+ st.rerun()
471
+ else:
472
+ st.error("No output video was generated. Check logs for details.")
473
+ if st.button("Return to Settings"):
474
+ return_to_input()
475
+ st.rerun()
476
+
477
+ except subprocess.CalledProcessError as e:
478
+ st.error(f"Error running morphing pipeline: {e}")
479
+ if st.button("Return to Settings"):
480
+ return_to_input()
481
+ st.rerun()
482
+
483
+ except Exception as e:
484
+ st.error(f"An error occurred during processing: {e}")
485
+ if st.button("Return to Settings"):
486
+ return_to_input()
487
+ st.rerun()
488
+
489
+ # =============== RESULT PAGE ===============
490
+ elif st.session_state.page == 'result':
491
+ st.markdown(
492
+ """
493
+ <div style="text-align: center;">
494
+ <h2>Morphing Complete! 🎉</h2>
495
+ <p>Your morphing video has been successfully generated. You can download it below.</p>
496
+ </div>
497
+ """,
498
+ unsafe_allow_html=True
499
+ )
500
+
501
+ # Show the result video and download button
502
+ try:
503
+ if st.session_state.final_video_path:
504
+ # Display video preview
505
+ video_file = open(st.session_state.final_video_path, 'rb')
506
+ video_bytes = video_file.read()
507
+ video_file.close()
508
+
509
+ st.video(video_bytes)
510
+
511
+ # Download button
512
+ st.download_button(
513
+ "Download Morphing Video",
514
+ data=video_bytes,
515
+ file_name="metamorph_result.mp4",
516
+ mime="video/mp4"
517
+ )
518
+ except Exception as e:
519
+ st.error(f"Error preparing video for download: {e}")
520
+
521
+ # Button to start a new project
522
+ if st.button("Start New Morphing Project"):
523
+ return_to_input()
524
+ st.rerun()
525
 
526
  if __name__ == "__main__":
527
  main()