Upload 59 files
Browse files- diffueraser/diffueraser.py +62 -11
- gradio_app.py +629 -118
diffueraser/diffueraser.py
CHANGED
|
@@ -166,7 +166,38 @@ def resize_frames(frames, size=None):
|
|
| 166 |
|
| 167 |
return frames
|
| 168 |
|
| 169 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 170 |
cap = cv2.VideoCapture(validation_mask)
|
| 171 |
if not cap.isOpened():
|
| 172 |
print("Error: Could not open mask video.")
|
|
@@ -189,15 +220,15 @@ def read_mask(validation_mask, fps, n_total_frames, img_size, mask_dilation_iter
|
|
| 189 |
if mask.size != img_size:
|
| 190 |
mask = mask.resize(img_size, Image.NEAREST)
|
| 191 |
mask = np.asarray(mask)
|
| 192 |
-
m =
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
mask = Image.fromarray(m
|
| 201 |
masks.append(mask)
|
| 202 |
|
| 203 |
masked_image = np.array(frames[idx])*(1-(np.array(mask)[:,:,np.newaxis].astype(np.float32)/255))
|
|
@@ -207,6 +238,16 @@ def read_mask(validation_mask, fps, n_total_frames, img_size, mask_dilation_iter
|
|
| 207 |
idx += 1
|
| 208 |
cap.release()
|
| 209 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
return masks, masked_images
|
| 211 |
|
| 212 |
def read_priori(priori, fps, n_total_frames, img_size):
|
|
@@ -394,6 +435,7 @@ class DiffuEraser:
|
|
| 394 |
|
| 395 |
def forward(self, validation_image, validation_mask, priori, output_path,
|
| 396 |
max_img_size = 1280, video_length=2, mask_dilation_iter=4,
|
|
|
|
| 397 |
nframes=22, seed=None, revision = None, guidance_scale=None, blended=True,
|
| 398 |
prompt="", negative_prompt="", request_id=None, output_fps=None, progress_callback=None):
|
| 399 |
validation_prompt = prompt or ""
|
|
@@ -429,6 +471,9 @@ class DiffuEraser:
|
|
| 429 |
max_img_size=max_img_size,
|
| 430 |
nframes=nframes,
|
| 431 |
mask_dilation_iter=mask_dilation_iter,
|
|
|
|
|
|
|
|
|
|
| 432 |
guidance_scale=guidance_scale_final,
|
| 433 |
seed="random" if seed is None else seed,
|
| 434 |
prompt_chars=len(validation_prompt),
|
|
@@ -458,7 +503,13 @@ class DiffuEraser:
|
|
| 458 |
_progress(0.04, "DiffuEraser: reading mask")
|
| 459 |
################ read mask ################
|
| 460 |
with timed_stage("diffueraser.read_mask", request_id=request_id, input=validation_mask):
|
| 461 |
-
validation_masks_input, validation_images_input = read_mask(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 462 |
log_event("diffueraser.read_mask", "loaded masks", request_id=request_id, masks=len(validation_masks_input), masked_images=len(validation_images_input))
|
| 463 |
|
| 464 |
_progress(0.07, "DiffuEraser: reading ProPainter priori")
|
|
|
|
| 166 |
|
| 167 |
return frames
|
| 168 |
|
| 169 |
+
def _odd_kernel_size(value):
|
| 170 |
+
value = max(0, int(value))
|
| 171 |
+
if value <= 0:
|
| 172 |
+
return 0
|
| 173 |
+
return value * 2 + 1
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
def refine_mask_array(mask, mask_refine_mode="Keep", mask_refine_iterations=0, mask_feather_px=0, mask_dilation_iter=0):
|
| 177 |
+
mode = str(mask_refine_mode)
|
| 178 |
+
refine_iterations = max(0, int(mask_refine_iterations))
|
| 179 |
+
dilation_iterations = max(0, int(mask_dilation_iter))
|
| 180 |
+
feather_px = max(0, int(mask_feather_px))
|
| 181 |
+
|
| 182 |
+
m = (np.asarray(mask) > 0).astype(np.uint8) * 255
|
| 183 |
+
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
|
| 184 |
+
if refine_iterations > 0 and mode == "Erode":
|
| 185 |
+
m = cv2.erode(m, kernel, iterations=refine_iterations)
|
| 186 |
+
elif refine_iterations > 0 and mode == "Dilate":
|
| 187 |
+
m = cv2.dilate(m, kernel, iterations=refine_iterations)
|
| 188 |
+
|
| 189 |
+
if dilation_iterations > 0:
|
| 190 |
+
m = cv2.dilate(m, kernel, iterations=dilation_iterations)
|
| 191 |
+
|
| 192 |
+
kernel_size = _odd_kernel_size(feather_px)
|
| 193 |
+
if kernel_size > 0:
|
| 194 |
+
m = cv2.GaussianBlur(m, (kernel_size, kernel_size), 0)
|
| 195 |
+
return m
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
def read_mask(
|
| 199 |
+
validation_mask, fps, n_total_frames, img_size, mask_dilation_iter, frames,
|
| 200 |
+
mask_refine_mode="Keep", mask_refine_iterations=0, mask_feather_px=0, request_id=None):
|
| 201 |
cap = cv2.VideoCapture(validation_mask)
|
| 202 |
if not cap.isOpened():
|
| 203 |
print("Error: Could not open mask video.")
|
|
|
|
| 220 |
if mask.size != img_size:
|
| 221 |
mask = mask.resize(img_size, Image.NEAREST)
|
| 222 |
mask = np.asarray(mask)
|
| 223 |
+
m = refine_mask_array(
|
| 224 |
+
mask,
|
| 225 |
+
mask_refine_mode=mask_refine_mode,
|
| 226 |
+
mask_refine_iterations=mask_refine_iterations,
|
| 227 |
+
mask_feather_px=mask_feather_px,
|
| 228 |
+
mask_dilation_iter=mask_dilation_iter,
|
| 229 |
+
)
|
| 230 |
+
|
| 231 |
+
mask = Image.fromarray(m)
|
| 232 |
masks.append(mask)
|
| 233 |
|
| 234 |
masked_image = np.array(frames[idx])*(1-(np.array(mask)[:,:,np.newaxis].astype(np.float32)/255))
|
|
|
|
| 238 |
idx += 1
|
| 239 |
cap.release()
|
| 240 |
|
| 241 |
+
log_event(
|
| 242 |
+
"diffueraser.read_mask",
|
| 243 |
+
"mask refinement applied",
|
| 244 |
+
request_id=request_id,
|
| 245 |
+
mask_refine_mode=mask_refine_mode,
|
| 246 |
+
mask_refine_iterations=mask_refine_iterations,
|
| 247 |
+
mask_feather_px=mask_feather_px,
|
| 248 |
+
mask_dilation_iter=mask_dilation_iter,
|
| 249 |
+
masks=len(masks),
|
| 250 |
+
)
|
| 251 |
return masks, masked_images
|
| 252 |
|
| 253 |
def read_priori(priori, fps, n_total_frames, img_size):
|
|
|
|
| 435 |
|
| 436 |
def forward(self, validation_image, validation_mask, priori, output_path,
|
| 437 |
max_img_size = 1280, video_length=2, mask_dilation_iter=4,
|
| 438 |
+
mask_refine_mode="Keep", mask_refine_iterations=0, mask_feather_px=0,
|
| 439 |
nframes=22, seed=None, revision = None, guidance_scale=None, blended=True,
|
| 440 |
prompt="", negative_prompt="", request_id=None, output_fps=None, progress_callback=None):
|
| 441 |
validation_prompt = prompt or ""
|
|
|
|
| 471 |
max_img_size=max_img_size,
|
| 472 |
nframes=nframes,
|
| 473 |
mask_dilation_iter=mask_dilation_iter,
|
| 474 |
+
mask_refine_mode=mask_refine_mode,
|
| 475 |
+
mask_refine_iterations=mask_refine_iterations,
|
| 476 |
+
mask_feather_px=mask_feather_px,
|
| 477 |
guidance_scale=guidance_scale_final,
|
| 478 |
seed="random" if seed is None else seed,
|
| 479 |
prompt_chars=len(validation_prompt),
|
|
|
|
| 503 |
_progress(0.04, "DiffuEraser: reading mask")
|
| 504 |
################ read mask ################
|
| 505 |
with timed_stage("diffueraser.read_mask", request_id=request_id, input=validation_mask):
|
| 506 |
+
validation_masks_input, validation_images_input = read_mask(
|
| 507 |
+
validation_mask, fps, video_len, img_size, mask_dilation_iter, frames,
|
| 508 |
+
mask_refine_mode=mask_refine_mode,
|
| 509 |
+
mask_refine_iterations=mask_refine_iterations,
|
| 510 |
+
mask_feather_px=mask_feather_px,
|
| 511 |
+
request_id=request_id,
|
| 512 |
+
)
|
| 513 |
log_event("diffueraser.read_mask", "loaded masks", request_id=request_id, masks=len(validation_masks_input), masked_images=len(validation_images_input))
|
| 514 |
|
| 515 |
_progress(0.07, "DiffuEraser: reading ProPainter priori")
|
gradio_app.py
CHANGED
|
@@ -14,12 +14,15 @@ except ImportError:
|
|
| 14 |
import datetime
|
| 15 |
import os
|
| 16 |
import platform
|
|
|
|
| 17 |
import time
|
| 18 |
import traceback
|
| 19 |
import uuid
|
| 20 |
from contextlib import contextmanager
|
| 21 |
|
|
|
|
| 22 |
import gradio as gr
|
|
|
|
| 23 |
import torch
|
| 24 |
from moviepy.editor import VideoFileClip
|
| 25 |
|
|
@@ -319,8 +322,12 @@ diffueraser_path = "weights/diffuEraser"
|
|
| 319 |
propainter_model_dir = "weights/propainter"
|
| 320 |
|
| 321 |
# Default Gradio runtime parameters.
|
|
|
|
| 322 |
DEFAULT_MAX_DURATION = 5
|
| 323 |
DEFAULT_MASK_DILATION_ITER = 8
|
|
|
|
|
|
|
|
|
|
| 324 |
DEFAULT_MAX_IMG_SIZE = 960
|
| 325 |
DEFAULT_REF_STRIDE = 10
|
| 326 |
DEFAULT_NEIGHBOR_LENGTH = 10
|
|
@@ -333,6 +340,10 @@ DEFAULT_GUIDANCE_SCALE = -1.0
|
|
| 333 |
DEFAULT_OUTPUT_FRAME_RATE = "Same as processed video"
|
| 334 |
DEFAULT_ZERO_GPU_DURATION_MODE = "Auto"
|
| 335 |
DEFAULT_ZERO_GPU_DURATION = 180
|
|
|
|
|
|
|
|
|
|
|
|
|
| 336 |
|
| 337 |
FRAME_RATE_CHOICES = ["Original", "24 fps", "15 fps", "12 fps"]
|
| 338 |
FRAME_RATE_MAP = {
|
|
@@ -359,6 +370,8 @@ PCM_STEP_CHOICES = [
|
|
| 359 |
"LCM-Like LoRA",
|
| 360 |
]
|
| 361 |
SCHEDULER_CHOICES = ["Auto", "TCD", "LCM", "UniPC", "DDIM", "Euler", "DPM++ 2M"]
|
|
|
|
|
|
|
| 362 |
ZERO_GPU_DURATION_MODE_CHOICES = ["Auto", "Manual"]
|
| 363 |
PCM_STEP_COUNT = {
|
| 364 |
"2-Step": 2,
|
|
@@ -372,30 +385,40 @@ PCM_STEP_COUNT = {
|
|
| 372 |
}
|
| 373 |
IS_ZEROGPU_SPACE = is_zerogpu_env()
|
| 374 |
PRESETS = {
|
| 375 |
-
"
|
| 376 |
-
"max_duration":
|
| 377 |
-
"max_img_size":
|
| 378 |
-
"mask_dilation_iter":
|
| 379 |
-
"
|
| 380 |
-
"
|
| 381 |
-
"
|
| 382 |
-
"
|
| 383 |
-
"
|
|
|
|
|
|
|
|
|
|
| 384 |
"output_frame_rate": DEFAULT_OUTPUT_FRAME_RATE,
|
| 385 |
"scheduler_name": DEFAULT_SCHEDULER,
|
| 386 |
"seed": DEFAULT_SEED,
|
| 387 |
"guidance_scale": DEFAULT_GUIDANCE_SCALE,
|
| 388 |
"zero_gpu_duration_mode": DEFAULT_ZERO_GPU_DURATION_MODE,
|
| 389 |
-
"zero_gpu_duration":
|
|
|
|
|
|
|
|
|
|
|
|
|
| 390 |
},
|
| 391 |
-
"
|
| 392 |
-
"max_duration":
|
| 393 |
-
"max_img_size":
|
| 394 |
-
"mask_dilation_iter":
|
|
|
|
|
|
|
|
|
|
| 395 |
"subvideo_length": DEFAULT_SUBVIDEO_LENGTH,
|
| 396 |
"ref_stride": DEFAULT_REF_STRIDE,
|
| 397 |
"neighbor_length": DEFAULT_NEIGHBOR_LENGTH,
|
| 398 |
-
"ckpt":
|
| 399 |
"frame_rate_limit": DEFAULT_FRAME_RATE_LIMIT,
|
| 400 |
"output_frame_rate": DEFAULT_OUTPUT_FRAME_RATE,
|
| 401 |
"scheduler_name": DEFAULT_SCHEDULER,
|
|
@@ -403,11 +426,64 @@ PRESETS = {
|
|
| 403 |
"guidance_scale": DEFAULT_GUIDANCE_SCALE,
|
| 404 |
"zero_gpu_duration_mode": DEFAULT_ZERO_GPU_DURATION_MODE,
|
| 405 |
"zero_gpu_duration": DEFAULT_ZERO_GPU_DURATION,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 406 |
},
|
| 407 |
-
"
|
| 408 |
"max_duration": 8,
|
| 409 |
"max_img_size": 1152,
|
| 410 |
"mask_dilation_iter": 8,
|
|
|
|
|
|
|
|
|
|
| 411 |
"subvideo_length": 60,
|
| 412 |
"ref_stride": 10,
|
| 413 |
"neighbor_length": 12,
|
|
@@ -419,9 +495,36 @@ PRESETS = {
|
|
| 419 |
"guidance_scale": DEFAULT_GUIDANCE_SCALE,
|
| 420 |
"zero_gpu_duration_mode": DEFAULT_ZERO_GPU_DURATION_MODE,
|
| 421 |
"zero_gpu_duration": DEFAULT_ZERO_GPU_DURATION,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 422 |
},
|
| 423 |
}
|
| 424 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 425 |
# Model setup
|
| 426 |
with timed_stage("startup.device"):
|
| 427 |
device = get_device()
|
|
@@ -476,11 +579,98 @@ def trim_video(
|
|
| 476 |
log_event("video.trimmed", label, request_id=request_id, **meta)
|
| 477 |
|
| 478 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 479 |
def apply_preset(
|
| 480 |
preset_name,
|
| 481 |
max_duration,
|
| 482 |
max_img_size,
|
| 483 |
mask_dilation_iter,
|
|
|
|
|
|
|
|
|
|
| 484 |
subvideo_length,
|
| 485 |
ref_stride,
|
| 486 |
neighbor_length,
|
|
@@ -492,14 +682,20 @@ def apply_preset(
|
|
| 492 |
guidance_scale,
|
| 493 |
zero_gpu_duration_mode,
|
| 494 |
zero_gpu_duration,
|
|
|
|
|
|
|
| 495 |
prompt,
|
| 496 |
negative_prompt,
|
| 497 |
):
|
| 498 |
if preset_name == "Custom":
|
| 499 |
return (
|
|
|
|
| 500 |
max_duration,
|
| 501 |
max_img_size,
|
| 502 |
mask_dilation_iter,
|
|
|
|
|
|
|
|
|
|
| 503 |
subvideo_length,
|
| 504 |
ref_stride,
|
| 505 |
neighbor_length,
|
|
@@ -511,15 +707,21 @@ def apply_preset(
|
|
| 511 |
guidance_scale,
|
| 512 |
zero_gpu_duration_mode,
|
| 513 |
zero_gpu_duration,
|
|
|
|
|
|
|
| 514 |
prompt,
|
| 515 |
negative_prompt,
|
| 516 |
)
|
| 517 |
|
| 518 |
preset = PRESETS[preset_name]
|
| 519 |
return (
|
|
|
|
| 520 |
preset["max_duration"],
|
| 521 |
preset["max_img_size"],
|
| 522 |
preset["mask_dilation_iter"],
|
|
|
|
|
|
|
|
|
|
| 523 |
preset["subvideo_length"],
|
| 524 |
preset["ref_stride"],
|
| 525 |
preset["neighbor_length"],
|
|
@@ -531,13 +733,72 @@ def apply_preset(
|
|
| 531 |
preset["guidance_scale"],
|
| 532 |
preset["zero_gpu_duration_mode"],
|
| 533 |
preset["zero_gpu_duration"],
|
| 534 |
-
|
| 535 |
-
|
|
|
|
|
|
|
| 536 |
)
|
| 537 |
|
| 538 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 539 |
def clear_inputs():
|
| 540 |
-
return None, None, None, None, None, None
|
| 541 |
|
| 542 |
|
| 543 |
def make_ui_progress(progress, request_id=None):
|
|
@@ -577,6 +838,110 @@ def make_stage_progress(progress_update, start, end):
|
|
| 577 |
|
| 578 |
|
| 579 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 580 |
def _clip_int(value, minimum=30, maximum=300):
|
| 581 |
return max(minimum, min(maximum, int(round(value))))
|
| 582 |
|
|
@@ -609,6 +974,7 @@ def estimate_zero_gpu_duration(
|
|
| 609 |
output_frame_rate=DEFAULT_OUTPUT_FRAME_RATE,
|
| 610 |
scheduler_name=DEFAULT_SCHEDULER,
|
| 611 |
guidance_scale=DEFAULT_GUIDANCE_SCALE,
|
|
|
|
| 612 |
):
|
| 613 |
max_duration = max(1, int(max_duration))
|
| 614 |
max_img_size = max(256, int(max_img_size))
|
|
@@ -634,9 +1000,10 @@ def estimate_zero_gpu_duration(
|
|
| 634 |
lora_cost = 4.0
|
| 635 |
scheduler_cost = 2.0 if scheduler_name != "Auto" else 0.0
|
| 636 |
guidance_cost = 4.0 if guidance_scale > 1.0 else 0.0
|
|
|
|
| 637 |
margin = 5.0
|
| 638 |
|
| 639 |
-
estimate = base_cost + trim_cost + propainter_cost + lora_cost + scheduler_cost + guidance_cost + frame_cost + margin
|
| 640 |
duration = _clip_int(estimate)
|
| 641 |
return duration, {
|
| 642 |
"fps": f"{fps:.2f}",
|
|
@@ -646,6 +1013,7 @@ def estimate_zero_gpu_duration(
|
|
| 646 |
"step_factor": f"{step_factor:.2f}",
|
| 647 |
"frame_cost_sec": f"{frame_cost:.1f}",
|
| 648 |
"propainter_cost_sec": f"{propainter_cost:.1f}",
|
|
|
|
| 649 |
"raw_estimate_sec": f"{estimate:.1f}",
|
| 650 |
"margin_sec": f"{margin:.1f}",
|
| 651 |
"duration_floor_sec": 30,
|
|
@@ -663,6 +1031,7 @@ def resolve_zero_gpu_duration(
|
|
| 663 |
output_frame_rate=DEFAULT_OUTPUT_FRAME_RATE,
|
| 664 |
scheduler_name=DEFAULT_SCHEDULER,
|
| 665 |
guidance_scale=DEFAULT_GUIDANCE_SCALE,
|
|
|
|
| 666 |
zero_gpu_duration_mode=DEFAULT_ZERO_GPU_DURATION_MODE,
|
| 667 |
zero_gpu_duration=DEFAULT_ZERO_GPU_DURATION,
|
| 668 |
):
|
|
@@ -676,6 +1045,7 @@ def resolve_zero_gpu_duration(
|
|
| 676 |
frame_rate_limit=frame_rate_limit,
|
| 677 |
scheduler_name=scheduler_name,
|
| 678 |
guidance_scale=guidance_scale,
|
|
|
|
| 679 |
)
|
| 680 |
mode = str(zero_gpu_duration_mode)
|
| 681 |
if mode == "Manual":
|
|
@@ -701,6 +1071,9 @@ def get_duration(
|
|
| 701 |
input_mask,
|
| 702 |
max_duration=DEFAULT_MAX_DURATION,
|
| 703 |
mask_dilation_iter=DEFAULT_MASK_DILATION_ITER,
|
|
|
|
|
|
|
|
|
|
| 704 |
max_img_size=DEFAULT_MAX_IMG_SIZE,
|
| 705 |
ref_stride=DEFAULT_REF_STRIDE,
|
| 706 |
neighbor_length=DEFAULT_NEIGHBOR_LENGTH,
|
|
@@ -713,6 +1086,8 @@ def get_duration(
|
|
| 713 |
guidance_scale=DEFAULT_GUIDANCE_SCALE,
|
| 714 |
zero_gpu_duration_mode=DEFAULT_ZERO_GPU_DURATION_MODE,
|
| 715 |
zero_gpu_duration=DEFAULT_ZERO_GPU_DURATION,
|
|
|
|
|
|
|
| 716 |
prompt="",
|
| 717 |
negative_prompt="",
|
| 718 |
*extra_args,
|
|
@@ -727,6 +1102,7 @@ def get_duration(
|
|
| 727 |
frame_rate_limit=frame_rate_limit,
|
| 728 |
scheduler_name=scheduler_name,
|
| 729 |
guidance_scale=guidance_scale,
|
|
|
|
| 730 |
zero_gpu_duration_mode=zero_gpu_duration_mode,
|
| 731 |
zero_gpu_duration=zero_gpu_duration,
|
| 732 |
)
|
|
@@ -742,6 +1118,9 @@ def infer(
|
|
| 742 |
input_mask,
|
| 743 |
max_duration=DEFAULT_MAX_DURATION,
|
| 744 |
mask_dilation_iter=DEFAULT_MASK_DILATION_ITER,
|
|
|
|
|
|
|
|
|
|
| 745 |
max_img_size=DEFAULT_MAX_IMG_SIZE,
|
| 746 |
ref_stride=DEFAULT_REF_STRIDE,
|
| 747 |
neighbor_length=DEFAULT_NEIGHBOR_LENGTH,
|
|
@@ -754,6 +1133,8 @@ def infer(
|
|
| 754 |
guidance_scale=DEFAULT_GUIDANCE_SCALE,
|
| 755 |
zero_gpu_duration_mode=DEFAULT_ZERO_GPU_DURATION_MODE,
|
| 756 |
zero_gpu_duration=DEFAULT_ZERO_GPU_DURATION,
|
|
|
|
|
|
|
| 757 |
prompt="",
|
| 758 |
negative_prompt="",
|
| 759 |
progress=gr.Progress(track_tqdm=True),
|
|
@@ -778,6 +1159,9 @@ def infer(
|
|
| 778 |
# Setup paths and parameters
|
| 779 |
max_duration = int(max_duration)
|
| 780 |
mask_dilation_iter = int(mask_dilation_iter)
|
|
|
|
|
|
|
|
|
|
| 781 |
max_img_size = int(max_img_size)
|
| 782 |
ref_stride = int(ref_stride)
|
| 783 |
neighbor_length = int(neighbor_length)
|
|
@@ -792,6 +1176,8 @@ def infer(
|
|
| 792 |
guidance_scale = None if guidance_scale_value < 0 else guidance_scale_value
|
| 793 |
zero_gpu_duration_mode = str(zero_gpu_duration_mode)
|
| 794 |
zero_gpu_duration = int(zero_gpu_duration)
|
|
|
|
|
|
|
| 795 |
prompt = str(prompt or "")
|
| 796 |
negative_prompt = str(negative_prompt or "")
|
| 797 |
|
|
@@ -804,6 +1190,7 @@ def infer(
|
|
| 804 |
frame_rate_limit=frame_rate_limit,
|
| 805 |
scheduler_name=scheduler_name,
|
| 806 |
guidance_scale=guidance_scale_value,
|
|
|
|
| 807 |
zero_gpu_duration_mode=zero_gpu_duration_mode,
|
| 808 |
zero_gpu_duration=zero_gpu_duration,
|
| 809 |
)
|
|
@@ -814,6 +1201,9 @@ def infer(
|
|
| 814 |
request_id=request_id,
|
| 815 |
max_duration=max_duration,
|
| 816 |
mask_dilation_iter=mask_dilation_iter,
|
|
|
|
|
|
|
|
|
|
| 817 |
max_img_size=max_img_size,
|
| 818 |
ref_stride=ref_stride,
|
| 819 |
neighbor_length=neighbor_length,
|
|
@@ -829,6 +1219,8 @@ def infer(
|
|
| 829 |
zero_gpu_duration_manual_slider=zero_gpu_duration_details["manual_slider_sec"],
|
| 830 |
zero_gpu_duration_auto_estimate=zero_gpu_duration_details["auto_estimate_sec"],
|
| 831 |
zero_gpu_duration_effective=zero_gpu_effective_duration,
|
|
|
|
|
|
|
| 832 |
prompt_chars=len(prompt),
|
| 833 |
negative_prompt_chars=len(negative_prompt),
|
| 834 |
)
|
|
@@ -836,7 +1228,9 @@ def infer(
|
|
| 836 |
# Timestamp for unique filenames
|
| 837 |
trimmed_video_path = os.path.join(save_path, f"trimmed_video_{timestamp}.mp4")
|
| 838 |
trimmed_mask_path = os.path.join(save_path, f"trimmed_mask_{timestamp}.mp4")
|
|
|
|
| 839 |
priori_path = os.path.join(save_path, f"priori_{timestamp}.mp4")
|
|
|
|
| 840 |
output_path = os.path.join(save_path, f"diffueraser_result_{timestamp}.mp4")
|
| 841 |
log_event(
|
| 842 |
"request.paths",
|
|
@@ -844,7 +1238,9 @@ def infer(
|
|
| 844 |
request_id=request_id,
|
| 845 |
trimmed_video=trimmed_video_path,
|
| 846 |
trimmed_mask=trimmed_mask_path,
|
|
|
|
| 847 |
priori=priori_path,
|
|
|
|
| 848 |
output=output_path,
|
| 849 |
)
|
| 850 |
|
|
@@ -866,6 +1262,21 @@ def infer(
|
|
| 866 |
request_id=request_id,
|
| 867 |
label="input_mask",
|
| 868 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 869 |
|
| 870 |
# Dynamically compute video_length in frames from the trimmed video.
|
| 871 |
with timed_stage("video.inspect_trimmed", request_id=request_id):
|
|
@@ -896,6 +1307,9 @@ def infer(
|
|
| 896 |
neighbor_length=neighbor_length,
|
| 897 |
subvideo_length=subvideo_length,
|
| 898 |
mask_dilation=mask_dilation_iter,
|
|
|
|
|
|
|
|
|
|
| 899 |
):
|
| 900 |
propainter.forward(trimmed_video_path, trimmed_mask_path, priori_path,
|
| 901 |
video_length=video_length, ref_stride=ref_stride,
|
|
@@ -920,15 +1334,30 @@ def infer(
|
|
| 920 |
guidance_scale=guidance_scale,
|
| 921 |
seed="random" if seed is None else seed,
|
| 922 |
):
|
| 923 |
-
video_inpainting_sd.forward(trimmed_video_path, trimmed_mask_path, priori_path,
|
| 924 |
max_img_size=max_img_size, video_length=video_length,
|
| 925 |
mask_dilation_iter=mask_dilation_iter,
|
|
|
|
|
|
|
|
|
|
| 926 |
seed=seed, guidance_scale=guidance_scale,
|
| 927 |
prompt=prompt, negative_prompt=negative_prompt,
|
| 928 |
request_id=request_id, output_fps=output_fps,
|
| 929 |
progress_callback=make_stage_progress(progress_update, 0.40, 0.90))
|
| 930 |
log_cuda_memory("after_diffueraser", request_id=request_id)
|
| 931 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 932 |
progress_update(0.92, desc="Finalizing outputs")
|
| 933 |
with timed_stage("video.inspect_output", request_id=request_id):
|
| 934 |
output_meta = video_metadata(output_path)
|
|
@@ -944,12 +1373,14 @@ def infer(
|
|
| 944 |
request_id=request_id,
|
| 945 |
total_elapsed_sec=f"{total_elapsed:.2f}",
|
| 946 |
output=output_path,
|
|
|
|
| 947 |
trimmed_video=trimmed_video_path,
|
|
|
|
| 948 |
priori=priori_path,
|
| 949 |
debug_log=debug_log_path,
|
| 950 |
)
|
| 951 |
progress_update(1.0, desc="Done")
|
| 952 |
-
return output_path, trimmed_video_path, priori_path, debug_log_path
|
| 953 |
|
| 954 |
except Exception as exc:
|
| 955 |
total_elapsed = time.perf_counter() - total_start
|
|
@@ -985,29 +1416,23 @@ with gr.Blocks() as demo:
|
|
| 985 |
input_video = gr.Video(label="Input Video (MP4 ONLY)")
|
| 986 |
input_mask = gr.Video(label="Input Mask Video (MP4 ONLY)")
|
| 987 |
with gr.Accordion("Basic settings", open=True):
|
| 988 |
-
gr.Markdown("
|
| 989 |
preset = gr.Dropdown(
|
| 990 |
-
label="
|
| 991 |
-
choices=[
|
| 992 |
-
|
| 993 |
-
|
| 994 |
-
|
| 995 |
-
|
| 996 |
-
|
| 997 |
-
|
| 998 |
-
|
| 999 |
-
|
| 1000 |
-
|
| 1001 |
-
label="Frame rate limit",
|
| 1002 |
-
choices=FRAME_RATE_CHOICES,
|
| 1003 |
-
value=DEFAULT_FRAME_RATE_LIMIT,
|
| 1004 |
-
)
|
| 1005 |
-
output_frame_rate = gr.Dropdown(
|
| 1006 |
-
label="Output frame rate",
|
| 1007 |
-
choices=OUTPUT_FRAME_RATE_CHOICES,
|
| 1008 |
-
value=DEFAULT_OUTPUT_FRAME_RATE,
|
| 1009 |
-
info="Can reduce the output video FPS without changing the inference frame rate.",
|
| 1010 |
)
|
|
|
|
|
|
|
|
|
|
| 1011 |
with gr.Row():
|
| 1012 |
max_duration = gr.Slider(
|
| 1013 |
label="Max clip duration (seconds)",
|
|
@@ -1016,91 +1441,159 @@ with gr.Blocks() as demo:
|
|
| 1016 |
value=DEFAULT_MAX_DURATION,
|
| 1017 |
step=1,
|
| 1018 |
)
|
| 1019 |
-
|
| 1020 |
-
label="
|
| 1021 |
-
|
| 1022 |
-
|
| 1023 |
-
|
| 1024 |
-
step=64,
|
| 1025 |
)
|
| 1026 |
-
mask_dilation_iter = gr.Slider(
|
| 1027 |
-
label="Mask dilation iterations",
|
| 1028 |
-
minimum=0,
|
| 1029 |
-
maximum=20,
|
| 1030 |
-
value=DEFAULT_MASK_DILATION_ITER,
|
| 1031 |
-
step=1,
|
| 1032 |
-
)
|
| 1033 |
|
| 1034 |
with gr.Accordion("Advanced settings", open=False):
|
| 1035 |
-
gr.Markdown("Optional controls.
|
| 1036 |
-
|
| 1037 |
-
|
| 1038 |
-
|
| 1039 |
-
|
| 1040 |
-
|
| 1041 |
-
|
| 1042 |
-
|
| 1043 |
-
|
| 1044 |
-
|
| 1045 |
-
|
| 1046 |
-
|
| 1047 |
-
|
| 1048 |
-
|
| 1049 |
-
|
| 1050 |
-
|
| 1051 |
-
|
| 1052 |
-
|
| 1053 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1054 |
seed = gr.Number(
|
| 1055 |
label="Seed (-1 for random)",
|
| 1056 |
value=DEFAULT_SEED,
|
| 1057 |
precision=0,
|
| 1058 |
)
|
| 1059 |
-
|
| 1060 |
-
|
| 1061 |
-
|
| 1062 |
-
|
| 1063 |
-
|
| 1064 |
-
|
| 1065 |
-
|
| 1066 |
-
|
| 1067 |
-
|
| 1068 |
-
|
| 1069 |
-
label="ZeroGPU duration mode",
|
| 1070 |
-
choices=ZERO_GPU_DURATION_MODE_CHOICES,
|
| 1071 |
-
value=DEFAULT_ZERO_GPU_DURATION_MODE,
|
| 1072 |
-
info="Auto estimates the reservation from the current settings. Normal GPU Spaces ignore this setting.",
|
| 1073 |
)
|
| 1074 |
-
|
| 1075 |
-
|
| 1076 |
-
|
| 1077 |
-
|
| 1078 |
-
|
| 1079 |
-
|
| 1080 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1081 |
)
|
| 1082 |
-
|
| 1083 |
-
|
| 1084 |
-
|
| 1085 |
-
|
| 1086 |
-
|
| 1087 |
-
|
| 1088 |
-
|
|
|
|
| 1089 |
)
|
| 1090 |
-
|
| 1091 |
-
label="
|
| 1092 |
-
minimum=
|
| 1093 |
-
maximum=
|
| 1094 |
-
value=
|
| 1095 |
-
step=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1096 |
)
|
| 1097 |
-
neighbor_length = gr.Slider(
|
| 1098 |
-
label="ProPainter neighbor length",
|
| 1099 |
-
minimum=2,
|
| 1100 |
-
maximum=30,
|
| 1101 |
-
value=DEFAULT_NEIGHBOR_LENGTH,
|
| 1102 |
-
step=2,
|
| 1103 |
-
)
|
| 1104 |
|
| 1105 |
with gr.Row():
|
| 1106 |
submit_btn = gr.Button("Run", variant="primary")
|
|
@@ -1110,7 +1603,9 @@ with gr.Blocks() as demo:
|
|
| 1110 |
video_result = gr.Video(label="Result", buttons=["download"])
|
| 1111 |
with gr.Accordion("Debug outputs", open=False):
|
| 1112 |
trimmed_video_result = gr.Video(label="Trimmed input video", interactive=False)
|
|
|
|
| 1113 |
priori_result = gr.Video(label="ProPainter priori video", interactive=False)
|
|
|
|
| 1114 |
debug_log_file = gr.File(label="Debug log file", interactive=False)
|
| 1115 |
gr.Examples(
|
| 1116 |
examples=[
|
|
@@ -1128,6 +1623,9 @@ with gr.Blocks() as demo:
|
|
| 1128 |
max_duration,
|
| 1129 |
max_img_size,
|
| 1130 |
mask_dilation_iter,
|
|
|
|
|
|
|
|
|
|
| 1131 |
subvideo_length,
|
| 1132 |
ref_stride,
|
| 1133 |
neighbor_length,
|
|
@@ -1139,13 +1637,19 @@ with gr.Blocks() as demo:
|
|
| 1139 |
guidance_scale,
|
| 1140 |
zero_gpu_duration_mode,
|
| 1141 |
zero_gpu_duration,
|
|
|
|
|
|
|
| 1142 |
prompt,
|
| 1143 |
negative_prompt,
|
| 1144 |
],
|
| 1145 |
outputs=[
|
|
|
|
| 1146 |
max_duration,
|
| 1147 |
max_img_size,
|
| 1148 |
mask_dilation_iter,
|
|
|
|
|
|
|
|
|
|
| 1149 |
subvideo_length,
|
| 1150 |
ref_stride,
|
| 1151 |
neighbor_length,
|
|
@@ -1157,6 +1661,8 @@ with gr.Blocks() as demo:
|
|
| 1157 |
guidance_scale,
|
| 1158 |
zero_gpu_duration_mode,
|
| 1159 |
zero_gpu_duration,
|
|
|
|
|
|
|
| 1160 |
prompt,
|
| 1161 |
negative_prompt,
|
| 1162 |
],
|
|
@@ -1165,7 +1671,7 @@ with gr.Blocks() as demo:
|
|
| 1165 |
clear_btn.click(
|
| 1166 |
fn=clear_inputs,
|
| 1167 |
inputs=None,
|
| 1168 |
-
outputs=[input_video, input_mask, video_result, trimmed_video_result, priori_result, debug_log_file],
|
| 1169 |
)
|
| 1170 |
|
| 1171 |
submit_btn.click(
|
|
@@ -1175,6 +1681,9 @@ with gr.Blocks() as demo:
|
|
| 1175 |
input_mask,
|
| 1176 |
max_duration,
|
| 1177 |
mask_dilation_iter,
|
|
|
|
|
|
|
|
|
|
| 1178 |
max_img_size,
|
| 1179 |
ref_stride,
|
| 1180 |
neighbor_length,
|
|
@@ -1187,10 +1696,12 @@ with gr.Blocks() as demo:
|
|
| 1187 |
guidance_scale,
|
| 1188 |
zero_gpu_duration_mode,
|
| 1189 |
zero_gpu_duration,
|
|
|
|
|
|
|
| 1190 |
prompt,
|
| 1191 |
negative_prompt,
|
| 1192 |
],
|
| 1193 |
-
outputs=[video_result, trimmed_video_result, priori_result, debug_log_file],
|
| 1194 |
)
|
| 1195 |
|
| 1196 |
demo.queue().launch(show_error=True, ssr_mode=False)
|
|
|
|
| 14 |
import datetime
|
| 15 |
import os
|
| 16 |
import platform
|
| 17 |
+
import shutil
|
| 18 |
import time
|
| 19 |
import traceback
|
| 20 |
import uuid
|
| 21 |
from contextlib import contextmanager
|
| 22 |
|
| 23 |
+
import cv2
|
| 24 |
import gradio as gr
|
| 25 |
+
import numpy as np
|
| 26 |
import torch
|
| 27 |
from moviepy.editor import VideoFileClip
|
| 28 |
|
|
|
|
| 322 |
propainter_model_dir = "weights/propainter"
|
| 323 |
|
| 324 |
# Default Gradio runtime parameters.
|
| 325 |
+
DEFAULT_PRESET = "Remove object / person"
|
| 326 |
DEFAULT_MAX_DURATION = 5
|
| 327 |
DEFAULT_MASK_DILATION_ITER = 8
|
| 328 |
+
DEFAULT_MASK_REFINE_MODE = "Keep"
|
| 329 |
+
DEFAULT_MASK_REFINE_ITERATIONS = 0
|
| 330 |
+
DEFAULT_MASK_FEATHER_PX = 0
|
| 331 |
DEFAULT_MAX_IMG_SIZE = 960
|
| 332 |
DEFAULT_REF_STRIDE = 10
|
| 333 |
DEFAULT_NEIGHBOR_LENGTH = 10
|
|
|
|
| 340 |
DEFAULT_OUTPUT_FRAME_RATE = "Same as processed video"
|
| 341 |
DEFAULT_ZERO_GPU_DURATION_MODE = "Auto"
|
| 342 |
DEFAULT_ZERO_GPU_DURATION = 180
|
| 343 |
+
DEFAULT_PROMPT = ""
|
| 344 |
+
DEFAULT_NEGATIVE_PROMPT = ""
|
| 345 |
+
DEFAULT_FACE_WATERMARK_MODE = "Off"
|
| 346 |
+
DEFAULT_FACE_CORRECTION_STRENGTH = 0.65
|
| 347 |
|
| 348 |
FRAME_RATE_CHOICES = ["Original", "24 fps", "15 fps", "12 fps"]
|
| 349 |
FRAME_RATE_MAP = {
|
|
|
|
| 370 |
"LCM-Like LoRA",
|
| 371 |
]
|
| 372 |
SCHEDULER_CHOICES = ["Auto", "TCD", "LCM", "UniPC", "DDIM", "Euler", "DPM++ 2M"]
|
| 373 |
+
MASK_REFINE_MODE_CHOICES = ["Erode", "Keep", "Dilate"]
|
| 374 |
+
FACE_WATERMARK_MODE_CHOICES = ["Off", "Preserve original face detail", "Use ProPainter-prior blend"]
|
| 375 |
ZERO_GPU_DURATION_MODE_CHOICES = ["Auto", "Manual"]
|
| 376 |
PCM_STEP_COUNT = {
|
| 377 |
"2-Step": 2,
|
|
|
|
| 385 |
}
|
| 386 |
IS_ZEROGPU_SPACE = is_zerogpu_env()
|
| 387 |
PRESETS = {
|
| 388 |
+
"Remove object / person": {
|
| 389 |
+
"max_duration": 5,
|
| 390 |
+
"max_img_size": 960,
|
| 391 |
+
"mask_dilation_iter": 8,
|
| 392 |
+
"mask_refine_mode": "Keep",
|
| 393 |
+
"mask_refine_iterations": 0,
|
| 394 |
+
"mask_feather_px": 0,
|
| 395 |
+
"subvideo_length": DEFAULT_SUBVIDEO_LENGTH,
|
| 396 |
+
"ref_stride": DEFAULT_REF_STRIDE,
|
| 397 |
+
"neighbor_length": DEFAULT_NEIGHBOR_LENGTH,
|
| 398 |
+
"ckpt": "4-Step",
|
| 399 |
+
"frame_rate_limit": DEFAULT_FRAME_RATE_LIMIT,
|
| 400 |
"output_frame_rate": DEFAULT_OUTPUT_FRAME_RATE,
|
| 401 |
"scheduler_name": DEFAULT_SCHEDULER,
|
| 402 |
"seed": DEFAULT_SEED,
|
| 403 |
"guidance_scale": DEFAULT_GUIDANCE_SCALE,
|
| 404 |
"zero_gpu_duration_mode": DEFAULT_ZERO_GPU_DURATION_MODE,
|
| 405 |
+
"zero_gpu_duration": DEFAULT_ZERO_GPU_DURATION,
|
| 406 |
+
"prompt": "",
|
| 407 |
+
"negative_prompt": "",
|
| 408 |
+
"face_watermark_mode": DEFAULT_FACE_WATERMARK_MODE,
|
| 409 |
+
"face_correction_strength": DEFAULT_FACE_CORRECTION_STRENGTH,
|
| 410 |
},
|
| 411 |
+
"Remove small watermark or logo": {
|
| 412 |
+
"max_duration": 5,
|
| 413 |
+
"max_img_size": 960,
|
| 414 |
+
"mask_dilation_iter": 4,
|
| 415 |
+
"mask_refine_mode": "Keep",
|
| 416 |
+
"mask_refine_iterations": 0,
|
| 417 |
+
"mask_feather_px": 4,
|
| 418 |
"subvideo_length": DEFAULT_SUBVIDEO_LENGTH,
|
| 419 |
"ref_stride": DEFAULT_REF_STRIDE,
|
| 420 |
"neighbor_length": DEFAULT_NEIGHBOR_LENGTH,
|
| 421 |
+
"ckpt": "4-Step",
|
| 422 |
"frame_rate_limit": DEFAULT_FRAME_RATE_LIMIT,
|
| 423 |
"output_frame_rate": DEFAULT_OUTPUT_FRAME_RATE,
|
| 424 |
"scheduler_name": DEFAULT_SCHEDULER,
|
|
|
|
| 426 |
"guidance_scale": DEFAULT_GUIDANCE_SCALE,
|
| 427 |
"zero_gpu_duration_mode": DEFAULT_ZERO_GPU_DURATION_MODE,
|
| 428 |
"zero_gpu_duration": DEFAULT_ZERO_GPU_DURATION,
|
| 429 |
+
"prompt": "",
|
| 430 |
+
"negative_prompt": "",
|
| 431 |
+
"face_watermark_mode": DEFAULT_FACE_WATERMARK_MODE,
|
| 432 |
+
"face_correction_strength": DEFAULT_FACE_CORRECTION_STRENGTH,
|
| 433 |
+
},
|
| 434 |
+
"Face-area watermark": {
|
| 435 |
+
"max_duration": 5,
|
| 436 |
+
"max_img_size": 1152,
|
| 437 |
+
"mask_dilation_iter": 2,
|
| 438 |
+
"mask_refine_mode": "Erode",
|
| 439 |
+
"mask_refine_iterations": 1,
|
| 440 |
+
"mask_feather_px": 4,
|
| 441 |
+
"subvideo_length": DEFAULT_SUBVIDEO_LENGTH,
|
| 442 |
+
"ref_stride": DEFAULT_REF_STRIDE,
|
| 443 |
+
"neighbor_length": DEFAULT_NEIGHBOR_LENGTH,
|
| 444 |
+
"ckpt": "8-Step",
|
| 445 |
+
"frame_rate_limit": "Original",
|
| 446 |
+
"output_frame_rate": DEFAULT_OUTPUT_FRAME_RATE,
|
| 447 |
+
"scheduler_name": DEFAULT_SCHEDULER,
|
| 448 |
+
"seed": DEFAULT_SEED,
|
| 449 |
+
"guidance_scale": DEFAULT_GUIDANCE_SCALE,
|
| 450 |
+
"zero_gpu_duration_mode": DEFAULT_ZERO_GPU_DURATION_MODE,
|
| 451 |
+
"zero_gpu_duration": DEFAULT_ZERO_GPU_DURATION,
|
| 452 |
+
"prompt": "",
|
| 453 |
+
"negative_prompt": "distorted face, deformed face, blurry face, warped mouth",
|
| 454 |
+
"face_watermark_mode": "Preserve original face detail",
|
| 455 |
+
"face_correction_strength": 0.65,
|
| 456 |
+
},
|
| 457 |
+
"Fast preview": {
|
| 458 |
+
"max_duration": 4,
|
| 459 |
+
"max_img_size": 768,
|
| 460 |
+
"mask_dilation_iter": 8,
|
| 461 |
+
"mask_refine_mode": "Keep",
|
| 462 |
+
"mask_refine_iterations": 0,
|
| 463 |
+
"mask_feather_px": 0,
|
| 464 |
+
"subvideo_length": 40,
|
| 465 |
+
"ref_stride": 12,
|
| 466 |
+
"neighbor_length": 8,
|
| 467 |
+
"ckpt": "2-Step",
|
| 468 |
+
"frame_rate_limit": "12 fps",
|
| 469 |
+
"output_frame_rate": DEFAULT_OUTPUT_FRAME_RATE,
|
| 470 |
+
"scheduler_name": DEFAULT_SCHEDULER,
|
| 471 |
+
"seed": DEFAULT_SEED,
|
| 472 |
+
"guidance_scale": DEFAULT_GUIDANCE_SCALE,
|
| 473 |
+
"zero_gpu_duration_mode": DEFAULT_ZERO_GPU_DURATION_MODE,
|
| 474 |
+
"zero_gpu_duration": 90,
|
| 475 |
+
"prompt": "",
|
| 476 |
+
"negative_prompt": "",
|
| 477 |
+
"face_watermark_mode": DEFAULT_FACE_WATERMARK_MODE,
|
| 478 |
+
"face_correction_strength": DEFAULT_FACE_CORRECTION_STRENGTH,
|
| 479 |
},
|
| 480 |
+
"High quality object removal": {
|
| 481 |
"max_duration": 8,
|
| 482 |
"max_img_size": 1152,
|
| 483 |
"mask_dilation_iter": 8,
|
| 484 |
+
"mask_refine_mode": "Keep",
|
| 485 |
+
"mask_refine_iterations": 0,
|
| 486 |
+
"mask_feather_px": 0,
|
| 487 |
"subvideo_length": 60,
|
| 488 |
"ref_stride": 10,
|
| 489 |
"neighbor_length": 12,
|
|
|
|
| 495 |
"guidance_scale": DEFAULT_GUIDANCE_SCALE,
|
| 496 |
"zero_gpu_duration_mode": DEFAULT_ZERO_GPU_DURATION_MODE,
|
| 497 |
"zero_gpu_duration": DEFAULT_ZERO_GPU_DURATION,
|
| 498 |
+
"prompt": "",
|
| 499 |
+
"negative_prompt": "",
|
| 500 |
+
"face_watermark_mode": DEFAULT_FACE_WATERMARK_MODE,
|
| 501 |
+
"face_correction_strength": DEFAULT_FACE_CORRECTION_STRENGTH,
|
| 502 |
},
|
| 503 |
}
|
| 504 |
|
| 505 |
+
TASK_MODE_SUMMARIES = {
|
| 506 |
+
"Remove object / person": "**Current mode:** object/person removal · wider mask · balanced quality.",
|
| 507 |
+
"Remove small watermark or logo": "**Current mode:** small logo/watermark · moderate mask · keeps normal inpainting behavior.",
|
| 508 |
+
"Face-area watermark": "**Current mode:** face-area watermark · small mask · original-face blend · slower.",
|
| 509 |
+
"Fast preview": "**Current mode:** fast preview · short clip · low FPS · lower quality.",
|
| 510 |
+
"High quality object removal": "**Current mode:** high quality object removal · larger image size · slower.",
|
| 511 |
+
"Custom": "**Current mode:** custom settings · Advanced values are used as-is.",
|
| 512 |
+
}
|
| 513 |
+
|
| 514 |
+
SITUATION_GUIDE = """
|
| 515 |
+
### Choose a task mode
|
| 516 |
+
|
| 517 |
+
| Situation | Recommended mode | Notes |
|
| 518 |
+
|---|---|---|
|
| 519 |
+
| Remove a whole person, object, or large unwanted region | **Remove object / person** | Best default. Uses a wider mask so object edges are less likely to remain. |
|
| 520 |
+
| Remove a small logo, subtitle, or watermark that is not on a face | **Remove small watermark or logo** | Uses moderate feathering and less dilation than full object removal. |
|
| 521 |
+
| Remove a small watermark over eyes, mouth, or other facial details | **Face-area watermark** | Preserves more original face detail. It may leave some watermark and is not for full-body removal. |
|
| 522 |
+
| Quickly check that the Space works | **Fast preview** | Lower quality, shorter duration, lower FPS. |
|
| 523 |
+
| Object/person removal with more quality and more runtime | **High quality object removal** | Slower. Useful after the normal object mode works. |
|
| 524 |
+
|
| 525 |
+
If the mask covers a large area, start with **Remove object / person**. Use **Face-area watermark** only for small masks over facial details.
|
| 526 |
+
"""
|
| 527 |
+
|
| 528 |
# Model setup
|
| 529 |
with timed_stage("startup.device"):
|
| 530 |
device = get_device()
|
|
|
|
| 579 |
log_event("video.trimmed", label, request_id=request_id, **meta)
|
| 580 |
|
| 581 |
|
| 582 |
+
def _odd_kernel_size(value):
|
| 583 |
+
value = max(0, int(value))
|
| 584 |
+
if value <= 0:
|
| 585 |
+
return 0
|
| 586 |
+
# Use a radius-like UI value and convert it to an odd Gaussian kernel size.
|
| 587 |
+
return value * 2 + 1
|
| 588 |
+
|
| 589 |
+
|
| 590 |
+
def refine_mask_array(mask, mask_refine_mode="Keep", mask_refine_iterations=0, mask_feather_px=0, mask_dilation_iter=0):
|
| 591 |
+
mode = str(mask_refine_mode)
|
| 592 |
+
refine_iterations = max(0, int(mask_refine_iterations))
|
| 593 |
+
dilation_iterations = max(0, int(mask_dilation_iter))
|
| 594 |
+
feather_px = max(0, int(mask_feather_px))
|
| 595 |
+
|
| 596 |
+
m = (np.asarray(mask) > 0).astype(np.uint8) * 255
|
| 597 |
+
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
|
| 598 |
+
if refine_iterations > 0 and mode == "Erode":
|
| 599 |
+
m = cv2.erode(m, kernel, iterations=refine_iterations)
|
| 600 |
+
elif refine_iterations > 0 and mode == "Dilate":
|
| 601 |
+
m = cv2.dilate(m, kernel, iterations=refine_iterations)
|
| 602 |
+
|
| 603 |
+
if dilation_iterations > 0:
|
| 604 |
+
m = cv2.dilate(m, kernel, iterations=dilation_iterations)
|
| 605 |
+
|
| 606 |
+
kernel_size = _odd_kernel_size(feather_px)
|
| 607 |
+
if kernel_size > 0:
|
| 608 |
+
m = cv2.GaussianBlur(m, (kernel_size, kernel_size), 0)
|
| 609 |
+
return m
|
| 610 |
+
|
| 611 |
+
|
| 612 |
+
def create_refined_mask_preview(
|
| 613 |
+
input_path,
|
| 614 |
+
output_path,
|
| 615 |
+
mask_refine_mode=DEFAULT_MASK_REFINE_MODE,
|
| 616 |
+
mask_refine_iterations=DEFAULT_MASK_REFINE_ITERATIONS,
|
| 617 |
+
mask_feather_px=DEFAULT_MASK_FEATHER_PX,
|
| 618 |
+
mask_dilation_iter=DEFAULT_MASK_DILATION_ITER,
|
| 619 |
+
request_id=None,
|
| 620 |
+
):
|
| 621 |
+
with timed_stage(
|
| 622 |
+
"mask.preview_refined",
|
| 623 |
+
request_id=request_id,
|
| 624 |
+
input=input_path,
|
| 625 |
+
output=output_path,
|
| 626 |
+
mask_refine_mode=mask_refine_mode,
|
| 627 |
+
mask_refine_iterations=mask_refine_iterations,
|
| 628 |
+
mask_feather_px=mask_feather_px,
|
| 629 |
+
mask_dilation_iter=mask_dilation_iter,
|
| 630 |
+
):
|
| 631 |
+
cap = cv2.VideoCapture(input_path)
|
| 632 |
+
if not cap.isOpened():
|
| 633 |
+
raise ValueError("Could not open mask video for refinement preview.")
|
| 634 |
+
fps = cap.get(cv2.CAP_PROP_FPS) or 15
|
| 635 |
+
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
|
| 636 |
+
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
| 637 |
+
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
|
| 638 |
+
writer = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
|
| 639 |
+
frames = 0
|
| 640 |
+
try:
|
| 641 |
+
while True:
|
| 642 |
+
ret, frame = cap.read()
|
| 643 |
+
if not ret:
|
| 644 |
+
break
|
| 645 |
+
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
| 646 |
+
refined = refine_mask_array(
|
| 647 |
+
gray,
|
| 648 |
+
mask_refine_mode=mask_refine_mode,
|
| 649 |
+
mask_refine_iterations=mask_refine_iterations,
|
| 650 |
+
mask_feather_px=mask_feather_px,
|
| 651 |
+
mask_dilation_iter=mask_dilation_iter,
|
| 652 |
+
)
|
| 653 |
+
writer.write(cv2.cvtColor(refined, cv2.COLOR_GRAY2BGR))
|
| 654 |
+
frames += 1
|
| 655 |
+
finally:
|
| 656 |
+
cap.release()
|
| 657 |
+
writer.release()
|
| 658 |
+
meta = video_metadata(output_path)
|
| 659 |
+
log_event("mask.preview_refined", "created refined mask preview", request_id=request_id, frames=frames, **meta)
|
| 660 |
+
|
| 661 |
+
|
| 662 |
+
def task_mode_summary(task_mode):
|
| 663 |
+
return TASK_MODE_SUMMARIES.get(str(task_mode), TASK_MODE_SUMMARIES["Custom"])
|
| 664 |
+
|
| 665 |
+
|
| 666 |
def apply_preset(
|
| 667 |
preset_name,
|
| 668 |
max_duration,
|
| 669 |
max_img_size,
|
| 670 |
mask_dilation_iter,
|
| 671 |
+
mask_refine_mode,
|
| 672 |
+
mask_refine_iterations,
|
| 673 |
+
mask_feather_px,
|
| 674 |
subvideo_length,
|
| 675 |
ref_stride,
|
| 676 |
neighbor_length,
|
|
|
|
| 682 |
guidance_scale,
|
| 683 |
zero_gpu_duration_mode,
|
| 684 |
zero_gpu_duration,
|
| 685 |
+
face_watermark_mode,
|
| 686 |
+
face_correction_strength,
|
| 687 |
prompt,
|
| 688 |
negative_prompt,
|
| 689 |
):
|
| 690 |
if preset_name == "Custom":
|
| 691 |
return (
|
| 692 |
+
task_mode_summary(preset_name),
|
| 693 |
max_duration,
|
| 694 |
max_img_size,
|
| 695 |
mask_dilation_iter,
|
| 696 |
+
mask_refine_mode,
|
| 697 |
+
mask_refine_iterations,
|
| 698 |
+
mask_feather_px,
|
| 699 |
subvideo_length,
|
| 700 |
ref_stride,
|
| 701 |
neighbor_length,
|
|
|
|
| 707 |
guidance_scale,
|
| 708 |
zero_gpu_duration_mode,
|
| 709 |
zero_gpu_duration,
|
| 710 |
+
face_watermark_mode,
|
| 711 |
+
face_correction_strength,
|
| 712 |
prompt,
|
| 713 |
negative_prompt,
|
| 714 |
)
|
| 715 |
|
| 716 |
preset = PRESETS[preset_name]
|
| 717 |
return (
|
| 718 |
+
task_mode_summary(preset_name),
|
| 719 |
preset["max_duration"],
|
| 720 |
preset["max_img_size"],
|
| 721 |
preset["mask_dilation_iter"],
|
| 722 |
+
preset["mask_refine_mode"],
|
| 723 |
+
preset["mask_refine_iterations"],
|
| 724 |
+
preset["mask_feather_px"],
|
| 725 |
preset["subvideo_length"],
|
| 726 |
preset["ref_stride"],
|
| 727 |
preset["neighbor_length"],
|
|
|
|
| 733 |
preset["guidance_scale"],
|
| 734 |
preset["zero_gpu_duration_mode"],
|
| 735 |
preset["zero_gpu_duration"],
|
| 736 |
+
preset.get("face_watermark_mode", DEFAULT_FACE_WATERMARK_MODE),
|
| 737 |
+
preset.get("face_correction_strength", DEFAULT_FACE_CORRECTION_STRENGTH),
|
| 738 |
+
preset.get("prompt", prompt),
|
| 739 |
+
preset.get("negative_prompt", negative_prompt),
|
| 740 |
)
|
| 741 |
|
| 742 |
|
| 743 |
+
def inspect_mask_for_warning(mask_path, face_watermark_mode, mask_refine_mode, request_id=None):
|
| 744 |
+
face_like_mode = str(face_watermark_mode) != "Off" or str(mask_refine_mode) == "Erode"
|
| 745 |
+
if not face_like_mode:
|
| 746 |
+
return
|
| 747 |
+
try:
|
| 748 |
+
cap = cv2.VideoCapture(mask_path)
|
| 749 |
+
if not cap.isOpened():
|
| 750 |
+
return
|
| 751 |
+
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT) or 0)
|
| 752 |
+
sample_stride = max(1, frame_count // 10) if frame_count else 1
|
| 753 |
+
ratios = []
|
| 754 |
+
bbox_ratios = []
|
| 755 |
+
index = 0
|
| 756 |
+
while True:
|
| 757 |
+
ret, frame = cap.read()
|
| 758 |
+
if not ret:
|
| 759 |
+
break
|
| 760 |
+
if index % sample_stride == 0:
|
| 761 |
+
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
| 762 |
+
mask = gray > 24
|
| 763 |
+
ratio = float(mask.mean())
|
| 764 |
+
ratios.append(ratio)
|
| 765 |
+
coords = cv2.findNonZero(mask.astype(np.uint8))
|
| 766 |
+
if coords is not None:
|
| 767 |
+
x, y, w, h = cv2.boundingRect(coords)
|
| 768 |
+
bbox_ratios.append((w * h) / float(mask.shape[0] * mask.shape[1]))
|
| 769 |
+
index += 1
|
| 770 |
+
cap.release()
|
| 771 |
+
if not ratios:
|
| 772 |
+
return
|
| 773 |
+
avg_ratio = sum(ratios) / len(ratios)
|
| 774 |
+
max_bbox_ratio = max(bbox_ratios) if bbox_ratios else 0.0
|
| 775 |
+
log_event(
|
| 776 |
+
"mask.warning_check",
|
| 777 |
+
"computed mask area",
|
| 778 |
+
request_id=request_id,
|
| 779 |
+
avg_mask_ratio=f"{avg_ratio:.3f}",
|
| 780 |
+
max_bbox_ratio=f"{max_bbox_ratio:.3f}",
|
| 781 |
+
samples=len(ratios),
|
| 782 |
+
face_watermark_mode=face_watermark_mode,
|
| 783 |
+
mask_refine_mode=mask_refine_mode,
|
| 784 |
+
)
|
| 785 |
+
if avg_ratio > 0.10 or max_bbox_ratio > 0.22:
|
| 786 |
+
gr.Warning(
|
| 787 |
+
"Face-area watermark settings are intended for small face-detail masks. "
|
| 788 |
+
"This mask looks fairly large, so Remove object / person may work better."
|
| 789 |
+
)
|
| 790 |
+
log_event(
|
| 791 |
+
"mask.warning_check",
|
| 792 |
+
"warning emitted",
|
| 793 |
+
request_id=request_id,
|
| 794 |
+
avg_mask_ratio=f"{avg_ratio:.3f}",
|
| 795 |
+
max_bbox_ratio=f"{max_bbox_ratio:.3f}",
|
| 796 |
+
)
|
| 797 |
+
except Exception as exc:
|
| 798 |
+
log_event("mask.warning_check", "failed", request_id=request_id, error_type=type(exc).__name__, error=str(exc))
|
| 799 |
+
|
| 800 |
def clear_inputs():
|
| 801 |
+
return None, None, None, None, None, None, None, None
|
| 802 |
|
| 803 |
|
| 804 |
def make_ui_progress(progress, request_id=None):
|
|
|
|
| 838 |
|
| 839 |
|
| 840 |
|
| 841 |
+
def blend_video_with_mask(
|
| 842 |
+
generated_path,
|
| 843 |
+
base_path,
|
| 844 |
+
mask_path,
|
| 845 |
+
output_path,
|
| 846 |
+
correction_strength=0.65,
|
| 847 |
+
request_id=None,
|
| 848 |
+
label="face_watermark_blend",
|
| 849 |
+
):
|
| 850 |
+
correction_strength = max(0.0, min(1.0, float(correction_strength)))
|
| 851 |
+
with timed_stage(
|
| 852 |
+
"postprocess.face_watermark_blend",
|
| 853 |
+
request_id=request_id,
|
| 854 |
+
generated=generated_path,
|
| 855 |
+
base=base_path,
|
| 856 |
+
mask=mask_path,
|
| 857 |
+
output=output_path,
|
| 858 |
+
correction_strength=f"{correction_strength:.2f}",
|
| 859 |
+
label=label,
|
| 860 |
+
):
|
| 861 |
+
gen_cap = cv2.VideoCapture(generated_path)
|
| 862 |
+
base_cap = cv2.VideoCapture(base_path)
|
| 863 |
+
mask_cap = cv2.VideoCapture(mask_path)
|
| 864 |
+
if not gen_cap.isOpened() or not base_cap.isOpened() or not mask_cap.isOpened():
|
| 865 |
+
raise ValueError("Could not open one or more videos for face watermark blending.")
|
| 866 |
+
|
| 867 |
+
fps = gen_cap.get(cv2.CAP_PROP_FPS) or 15
|
| 868 |
+
width = int(gen_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
|
| 869 |
+
height = int(gen_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
| 870 |
+
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
|
| 871 |
+
writer = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
|
| 872 |
+
frames = 0
|
| 873 |
+
try:
|
| 874 |
+
while True:
|
| 875 |
+
ret_gen, gen = gen_cap.read()
|
| 876 |
+
ret_base, base = base_cap.read()
|
| 877 |
+
ret_mask, mask_frame = mask_cap.read()
|
| 878 |
+
if not ret_gen:
|
| 879 |
+
break
|
| 880 |
+
if not ret_base:
|
| 881 |
+
base = gen
|
| 882 |
+
if not ret_mask:
|
| 883 |
+
mask_frame = np.zeros_like(gen)
|
| 884 |
+
|
| 885 |
+
if base.shape[:2] != gen.shape[:2]:
|
| 886 |
+
base = cv2.resize(base, (width, height), interpolation=cv2.INTER_LINEAR)
|
| 887 |
+
if mask_frame.shape[:2] != gen.shape[:2]:
|
| 888 |
+
mask_frame = cv2.resize(mask_frame, (width, height), interpolation=cv2.INTER_LINEAR)
|
| 889 |
+
|
| 890 |
+
gray = cv2.cvtColor(mask_frame, cv2.COLOR_BGR2GRAY).astype(np.float32) / 255.0
|
| 891 |
+
# The mask controls where to preserve base details. Outside the mask, keep DiffuEraser's output.
|
| 892 |
+
preserve_alpha = gray * (1.0 - correction_strength)
|
| 893 |
+
preserve_alpha = preserve_alpha[:, :, None]
|
| 894 |
+
blended = gen.astype(np.float32) * (1.0 - preserve_alpha) + base.astype(np.float32) * preserve_alpha
|
| 895 |
+
writer.write(np.clip(blended, 0, 255).astype(np.uint8))
|
| 896 |
+
frames += 1
|
| 897 |
+
finally:
|
| 898 |
+
gen_cap.release()
|
| 899 |
+
base_cap.release()
|
| 900 |
+
mask_cap.release()
|
| 901 |
+
writer.release()
|
| 902 |
+
|
| 903 |
+
meta = video_metadata(output_path)
|
| 904 |
+
log_event("postprocess.face_watermark_blend", "created blended video", request_id=request_id, frames=frames, **meta)
|
| 905 |
+
|
| 906 |
+
|
| 907 |
+
def finalize_output_video(
|
| 908 |
+
raw_output_path,
|
| 909 |
+
trimmed_video_path,
|
| 910 |
+
priori_path,
|
| 911 |
+
refined_mask_path,
|
| 912 |
+
output_path,
|
| 913 |
+
face_watermark_mode=DEFAULT_FACE_WATERMARK_MODE,
|
| 914 |
+
face_correction_strength=DEFAULT_FACE_CORRECTION_STRENGTH,
|
| 915 |
+
request_id=None,
|
| 916 |
+
):
|
| 917 |
+
mode = str(face_watermark_mode)
|
| 918 |
+
if mode == "Preserve original face detail":
|
| 919 |
+
blend_video_with_mask(
|
| 920 |
+
raw_output_path,
|
| 921 |
+
trimmed_video_path,
|
| 922 |
+
refined_mask_path,
|
| 923 |
+
output_path,
|
| 924 |
+
correction_strength=face_correction_strength,
|
| 925 |
+
request_id=request_id,
|
| 926 |
+
label=mode,
|
| 927 |
+
)
|
| 928 |
+
elif mode == "Use ProPainter-prior blend":
|
| 929 |
+
blend_video_with_mask(
|
| 930 |
+
raw_output_path,
|
| 931 |
+
priori_path,
|
| 932 |
+
refined_mask_path,
|
| 933 |
+
output_path,
|
| 934 |
+
correction_strength=face_correction_strength,
|
| 935 |
+
request_id=request_id,
|
| 936 |
+
label=mode,
|
| 937 |
+
)
|
| 938 |
+
else:
|
| 939 |
+
with timed_stage("postprocess.finalize_output", request_id=request_id, mode=mode, raw_output=raw_output_path, output=output_path):
|
| 940 |
+
shutil.copyfile(raw_output_path, output_path)
|
| 941 |
+
meta = video_metadata(output_path)
|
| 942 |
+
log_event("postprocess.finalize_output", "copied raw DiffuEraser output", request_id=request_id, **meta)
|
| 943 |
+
|
| 944 |
+
|
| 945 |
def _clip_int(value, minimum=30, maximum=300):
|
| 946 |
return max(minimum, min(maximum, int(round(value))))
|
| 947 |
|
|
|
|
| 974 |
output_frame_rate=DEFAULT_OUTPUT_FRAME_RATE,
|
| 975 |
scheduler_name=DEFAULT_SCHEDULER,
|
| 976 |
guidance_scale=DEFAULT_GUIDANCE_SCALE,
|
| 977 |
+
face_watermark_mode=DEFAULT_FACE_WATERMARK_MODE,
|
| 978 |
):
|
| 979 |
max_duration = max(1, int(max_duration))
|
| 980 |
max_img_size = max(256, int(max_img_size))
|
|
|
|
| 1000 |
lora_cost = 4.0
|
| 1001 |
scheduler_cost = 2.0 if scheduler_name != "Auto" else 0.0
|
| 1002 |
guidance_cost = 4.0 if guidance_scale > 1.0 else 0.0
|
| 1003 |
+
postprocess_cost = 0.0 if str(face_watermark_mode) == "Off" else max(1.0, 0.015 * estimated_frames)
|
| 1004 |
margin = 5.0
|
| 1005 |
|
| 1006 |
+
estimate = base_cost + trim_cost + propainter_cost + lora_cost + scheduler_cost + guidance_cost + frame_cost + postprocess_cost + margin
|
| 1007 |
duration = _clip_int(estimate)
|
| 1008 |
return duration, {
|
| 1009 |
"fps": f"{fps:.2f}",
|
|
|
|
| 1013 |
"step_factor": f"{step_factor:.2f}",
|
| 1014 |
"frame_cost_sec": f"{frame_cost:.1f}",
|
| 1015 |
"propainter_cost_sec": f"{propainter_cost:.1f}",
|
| 1016 |
+
"postprocess_cost_sec": f"{postprocess_cost:.1f}",
|
| 1017 |
"raw_estimate_sec": f"{estimate:.1f}",
|
| 1018 |
"margin_sec": f"{margin:.1f}",
|
| 1019 |
"duration_floor_sec": 30,
|
|
|
|
| 1031 |
output_frame_rate=DEFAULT_OUTPUT_FRAME_RATE,
|
| 1032 |
scheduler_name=DEFAULT_SCHEDULER,
|
| 1033 |
guidance_scale=DEFAULT_GUIDANCE_SCALE,
|
| 1034 |
+
face_watermark_mode=DEFAULT_FACE_WATERMARK_MODE,
|
| 1035 |
zero_gpu_duration_mode=DEFAULT_ZERO_GPU_DURATION_MODE,
|
| 1036 |
zero_gpu_duration=DEFAULT_ZERO_GPU_DURATION,
|
| 1037 |
):
|
|
|
|
| 1045 |
frame_rate_limit=frame_rate_limit,
|
| 1046 |
scheduler_name=scheduler_name,
|
| 1047 |
guidance_scale=guidance_scale,
|
| 1048 |
+
face_watermark_mode=face_watermark_mode,
|
| 1049 |
)
|
| 1050 |
mode = str(zero_gpu_duration_mode)
|
| 1051 |
if mode == "Manual":
|
|
|
|
| 1071 |
input_mask,
|
| 1072 |
max_duration=DEFAULT_MAX_DURATION,
|
| 1073 |
mask_dilation_iter=DEFAULT_MASK_DILATION_ITER,
|
| 1074 |
+
mask_refine_mode=DEFAULT_MASK_REFINE_MODE,
|
| 1075 |
+
mask_refine_iterations=DEFAULT_MASK_REFINE_ITERATIONS,
|
| 1076 |
+
mask_feather_px=DEFAULT_MASK_FEATHER_PX,
|
| 1077 |
max_img_size=DEFAULT_MAX_IMG_SIZE,
|
| 1078 |
ref_stride=DEFAULT_REF_STRIDE,
|
| 1079 |
neighbor_length=DEFAULT_NEIGHBOR_LENGTH,
|
|
|
|
| 1086 |
guidance_scale=DEFAULT_GUIDANCE_SCALE,
|
| 1087 |
zero_gpu_duration_mode=DEFAULT_ZERO_GPU_DURATION_MODE,
|
| 1088 |
zero_gpu_duration=DEFAULT_ZERO_GPU_DURATION,
|
| 1089 |
+
face_watermark_mode=DEFAULT_FACE_WATERMARK_MODE,
|
| 1090 |
+
face_correction_strength=DEFAULT_FACE_CORRECTION_STRENGTH,
|
| 1091 |
prompt="",
|
| 1092 |
negative_prompt="",
|
| 1093 |
*extra_args,
|
|
|
|
| 1102 |
frame_rate_limit=frame_rate_limit,
|
| 1103 |
scheduler_name=scheduler_name,
|
| 1104 |
guidance_scale=guidance_scale,
|
| 1105 |
+
face_watermark_mode=face_watermark_mode,
|
| 1106 |
zero_gpu_duration_mode=zero_gpu_duration_mode,
|
| 1107 |
zero_gpu_duration=zero_gpu_duration,
|
| 1108 |
)
|
|
|
|
| 1118 |
input_mask,
|
| 1119 |
max_duration=DEFAULT_MAX_DURATION,
|
| 1120 |
mask_dilation_iter=DEFAULT_MASK_DILATION_ITER,
|
| 1121 |
+
mask_refine_mode=DEFAULT_MASK_REFINE_MODE,
|
| 1122 |
+
mask_refine_iterations=DEFAULT_MASK_REFINE_ITERATIONS,
|
| 1123 |
+
mask_feather_px=DEFAULT_MASK_FEATHER_PX,
|
| 1124 |
max_img_size=DEFAULT_MAX_IMG_SIZE,
|
| 1125 |
ref_stride=DEFAULT_REF_STRIDE,
|
| 1126 |
neighbor_length=DEFAULT_NEIGHBOR_LENGTH,
|
|
|
|
| 1133 |
guidance_scale=DEFAULT_GUIDANCE_SCALE,
|
| 1134 |
zero_gpu_duration_mode=DEFAULT_ZERO_GPU_DURATION_MODE,
|
| 1135 |
zero_gpu_duration=DEFAULT_ZERO_GPU_DURATION,
|
| 1136 |
+
face_watermark_mode=DEFAULT_FACE_WATERMARK_MODE,
|
| 1137 |
+
face_correction_strength=DEFAULT_FACE_CORRECTION_STRENGTH,
|
| 1138 |
prompt="",
|
| 1139 |
negative_prompt="",
|
| 1140 |
progress=gr.Progress(track_tqdm=True),
|
|
|
|
| 1159 |
# Setup paths and parameters
|
| 1160 |
max_duration = int(max_duration)
|
| 1161 |
mask_dilation_iter = int(mask_dilation_iter)
|
| 1162 |
+
mask_refine_mode = str(mask_refine_mode)
|
| 1163 |
+
mask_refine_iterations = int(mask_refine_iterations)
|
| 1164 |
+
mask_feather_px = int(mask_feather_px)
|
| 1165 |
max_img_size = int(max_img_size)
|
| 1166 |
ref_stride = int(ref_stride)
|
| 1167 |
neighbor_length = int(neighbor_length)
|
|
|
|
| 1176 |
guidance_scale = None if guidance_scale_value < 0 else guidance_scale_value
|
| 1177 |
zero_gpu_duration_mode = str(zero_gpu_duration_mode)
|
| 1178 |
zero_gpu_duration = int(zero_gpu_duration)
|
| 1179 |
+
face_watermark_mode = str(face_watermark_mode)
|
| 1180 |
+
face_correction_strength = float(face_correction_strength)
|
| 1181 |
prompt = str(prompt or "")
|
| 1182 |
negative_prompt = str(negative_prompt or "")
|
| 1183 |
|
|
|
|
| 1190 |
frame_rate_limit=frame_rate_limit,
|
| 1191 |
scheduler_name=scheduler_name,
|
| 1192 |
guidance_scale=guidance_scale_value,
|
| 1193 |
+
face_watermark_mode=face_watermark_mode,
|
| 1194 |
zero_gpu_duration_mode=zero_gpu_duration_mode,
|
| 1195 |
zero_gpu_duration=zero_gpu_duration,
|
| 1196 |
)
|
|
|
|
| 1201 |
request_id=request_id,
|
| 1202 |
max_duration=max_duration,
|
| 1203 |
mask_dilation_iter=mask_dilation_iter,
|
| 1204 |
+
mask_refine_mode=mask_refine_mode,
|
| 1205 |
+
mask_refine_iterations=mask_refine_iterations,
|
| 1206 |
+
mask_feather_px=mask_feather_px,
|
| 1207 |
max_img_size=max_img_size,
|
| 1208 |
ref_stride=ref_stride,
|
| 1209 |
neighbor_length=neighbor_length,
|
|
|
|
| 1219 |
zero_gpu_duration_manual_slider=zero_gpu_duration_details["manual_slider_sec"],
|
| 1220 |
zero_gpu_duration_auto_estimate=zero_gpu_duration_details["auto_estimate_sec"],
|
| 1221 |
zero_gpu_duration_effective=zero_gpu_effective_duration,
|
| 1222 |
+
face_watermark_mode=face_watermark_mode,
|
| 1223 |
+
face_correction_strength=f"{face_correction_strength:.2f}",
|
| 1224 |
prompt_chars=len(prompt),
|
| 1225 |
negative_prompt_chars=len(negative_prompt),
|
| 1226 |
)
|
|
|
|
| 1228 |
# Timestamp for unique filenames
|
| 1229 |
trimmed_video_path = os.path.join(save_path, f"trimmed_video_{timestamp}.mp4")
|
| 1230 |
trimmed_mask_path = os.path.join(save_path, f"trimmed_mask_{timestamp}.mp4")
|
| 1231 |
+
refined_mask_path = os.path.join(save_path, f"refined_mask_{timestamp}.mp4")
|
| 1232 |
priori_path = os.path.join(save_path, f"priori_{timestamp}.mp4")
|
| 1233 |
+
raw_output_path = os.path.join(save_path, f"diffueraser_raw_{timestamp}.mp4")
|
| 1234 |
output_path = os.path.join(save_path, f"diffueraser_result_{timestamp}.mp4")
|
| 1235 |
log_event(
|
| 1236 |
"request.paths",
|
|
|
|
| 1238 |
request_id=request_id,
|
| 1239 |
trimmed_video=trimmed_video_path,
|
| 1240 |
trimmed_mask=trimmed_mask_path,
|
| 1241 |
+
refined_mask=refined_mask_path,
|
| 1242 |
priori=priori_path,
|
| 1243 |
+
raw_output=raw_output_path,
|
| 1244 |
output=output_path,
|
| 1245 |
)
|
| 1246 |
|
|
|
|
| 1262 |
request_id=request_id,
|
| 1263 |
label="input_mask",
|
| 1264 |
)
|
| 1265 |
+
create_refined_mask_preview(
|
| 1266 |
+
trimmed_mask_path,
|
| 1267 |
+
refined_mask_path,
|
| 1268 |
+
mask_refine_mode=mask_refine_mode,
|
| 1269 |
+
mask_refine_iterations=mask_refine_iterations,
|
| 1270 |
+
mask_feather_px=mask_feather_px,
|
| 1271 |
+
mask_dilation_iter=mask_dilation_iter,
|
| 1272 |
+
request_id=request_id,
|
| 1273 |
+
)
|
| 1274 |
+
inspect_mask_for_warning(
|
| 1275 |
+
refined_mask_path,
|
| 1276 |
+
face_watermark_mode=face_watermark_mode,
|
| 1277 |
+
mask_refine_mode=mask_refine_mode,
|
| 1278 |
+
request_id=request_id,
|
| 1279 |
+
)
|
| 1280 |
|
| 1281 |
# Dynamically compute video_length in frames from the trimmed video.
|
| 1282 |
with timed_stage("video.inspect_trimmed", request_id=request_id):
|
|
|
|
| 1307 |
neighbor_length=neighbor_length,
|
| 1308 |
subvideo_length=subvideo_length,
|
| 1309 |
mask_dilation=mask_dilation_iter,
|
| 1310 |
+
mask_refine_mode=mask_refine_mode,
|
| 1311 |
+
mask_refine_iterations=mask_refine_iterations,
|
| 1312 |
+
mask_feather_px=mask_feather_px,
|
| 1313 |
):
|
| 1314 |
propainter.forward(trimmed_video_path, trimmed_mask_path, priori_path,
|
| 1315 |
video_length=video_length, ref_stride=ref_stride,
|
|
|
|
| 1334 |
guidance_scale=guidance_scale,
|
| 1335 |
seed="random" if seed is None else seed,
|
| 1336 |
):
|
| 1337 |
+
video_inpainting_sd.forward(trimmed_video_path, trimmed_mask_path, priori_path, raw_output_path,
|
| 1338 |
max_img_size=max_img_size, video_length=video_length,
|
| 1339 |
mask_dilation_iter=mask_dilation_iter,
|
| 1340 |
+
mask_refine_mode=mask_refine_mode,
|
| 1341 |
+
mask_refine_iterations=mask_refine_iterations,
|
| 1342 |
+
mask_feather_px=mask_feather_px,
|
| 1343 |
seed=seed, guidance_scale=guidance_scale,
|
| 1344 |
prompt=prompt, negative_prompt=negative_prompt,
|
| 1345 |
request_id=request_id, output_fps=output_fps,
|
| 1346 |
progress_callback=make_stage_progress(progress_update, 0.40, 0.90))
|
| 1347 |
log_cuda_memory("after_diffueraser", request_id=request_id)
|
| 1348 |
|
| 1349 |
+
progress_update(0.90, desc="Applying final blend")
|
| 1350 |
+
finalize_output_video(
|
| 1351 |
+
raw_output_path,
|
| 1352 |
+
trimmed_video_path,
|
| 1353 |
+
priori_path,
|
| 1354 |
+
refined_mask_path,
|
| 1355 |
+
output_path,
|
| 1356 |
+
face_watermark_mode=face_watermark_mode,
|
| 1357 |
+
face_correction_strength=face_correction_strength,
|
| 1358 |
+
request_id=request_id,
|
| 1359 |
+
)
|
| 1360 |
+
|
| 1361 |
progress_update(0.92, desc="Finalizing outputs")
|
| 1362 |
with timed_stage("video.inspect_output", request_id=request_id):
|
| 1363 |
output_meta = video_metadata(output_path)
|
|
|
|
| 1373 |
request_id=request_id,
|
| 1374 |
total_elapsed_sec=f"{total_elapsed:.2f}",
|
| 1375 |
output=output_path,
|
| 1376 |
+
raw_output=raw_output_path,
|
| 1377 |
trimmed_video=trimmed_video_path,
|
| 1378 |
+
refined_mask=refined_mask_path,
|
| 1379 |
priori=priori_path,
|
| 1380 |
debug_log=debug_log_path,
|
| 1381 |
)
|
| 1382 |
progress_update(1.0, desc="Done")
|
| 1383 |
+
return output_path, trimmed_video_path, refined_mask_path, priori_path, raw_output_path, debug_log_path
|
| 1384 |
|
| 1385 |
except Exception as exc:
|
| 1386 |
total_elapsed = time.perf_counter() - total_start
|
|
|
|
| 1416 |
input_video = gr.Video(label="Input Video (MP4 ONLY)")
|
| 1417 |
input_mask = gr.Video(label="Input Mask Video (MP4 ONLY)")
|
| 1418 |
with gr.Accordion("Basic settings", open=True):
|
| 1419 |
+
gr.Markdown("Choose a **Task mode** first. Most users only need this and **Max clip duration**.")
|
| 1420 |
preset = gr.Dropdown(
|
| 1421 |
+
label="Task mode",
|
| 1422 |
+
choices=[
|
| 1423 |
+
"Remove object / person",
|
| 1424 |
+
"Remove small watermark or logo",
|
| 1425 |
+
"Face-area watermark",
|
| 1426 |
+
"Fast preview",
|
| 1427 |
+
"High quality object removal",
|
| 1428 |
+
"Custom",
|
| 1429 |
+
],
|
| 1430 |
+
value=DEFAULT_PRESET,
|
| 1431 |
+
info="Start with the mode that matches what you want to remove.",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1432 |
)
|
| 1433 |
+
settings_summary = gr.Markdown(task_mode_summary(DEFAULT_PRESET))
|
| 1434 |
+
with gr.Accordion("Which mode should I choose?", open=False):
|
| 1435 |
+
gr.Markdown(SITUATION_GUIDE)
|
| 1436 |
with gr.Row():
|
| 1437 |
max_duration = gr.Slider(
|
| 1438 |
label="Max clip duration (seconds)",
|
|
|
|
| 1441 |
value=DEFAULT_MAX_DURATION,
|
| 1442 |
step=1,
|
| 1443 |
)
|
| 1444 |
+
frame_rate_limit = gr.Dropdown(
|
| 1445 |
+
label="Frame rate limit",
|
| 1446 |
+
choices=FRAME_RATE_CHOICES,
|
| 1447 |
+
value=DEFAULT_FRAME_RATE_LIMIT,
|
| 1448 |
+
info="Use Original for quality; lower FPS can speed up previews.",
|
|
|
|
| 1449 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1450 |
|
| 1451 |
with gr.Accordion("Advanced settings", open=False):
|
| 1452 |
+
gr.Markdown("Optional controls. Keep the task mode defaults unless you are tuning a difficult case.")
|
| 1453 |
+
|
| 1454 |
+
with gr.Accordion("Quality and speed", open=False):
|
| 1455 |
+
with gr.Row():
|
| 1456 |
+
ckpt = gr.Dropdown(
|
| 1457 |
+
label="PCM checkpoint",
|
| 1458 |
+
choices=PCM_STEP_CHOICES,
|
| 1459 |
+
value=DEFAULT_CKPT,
|
| 1460 |
+
info="More steps can improve quality but take longer.",
|
| 1461 |
+
)
|
| 1462 |
+
scheduler_name = gr.Dropdown(
|
| 1463 |
+
label="Scheduler",
|
| 1464 |
+
choices=SCHEDULER_CHOICES,
|
| 1465 |
+
value=DEFAULT_SCHEDULER,
|
| 1466 |
+
info="Auto keeps the default PCM scheduler behavior.",
|
| 1467 |
+
)
|
| 1468 |
+
with gr.Row():
|
| 1469 |
+
max_img_size = gr.Slider(
|
| 1470 |
+
label="Maximum image size",
|
| 1471 |
+
minimum=512,
|
| 1472 |
+
maximum=1280,
|
| 1473 |
+
value=DEFAULT_MAX_IMG_SIZE,
|
| 1474 |
+
step=64,
|
| 1475 |
+
info="Higher values preserve detail but are slower.",
|
| 1476 |
+
)
|
| 1477 |
+
output_frame_rate = gr.Dropdown(
|
| 1478 |
+
label="Output frame rate",
|
| 1479 |
+
choices=OUTPUT_FRAME_RATE_CHOICES,
|
| 1480 |
+
value=DEFAULT_OUTPUT_FRAME_RATE,
|
| 1481 |
+
info="Can reduce the output video FPS without changing the inference frame rate.",
|
| 1482 |
+
)
|
| 1483 |
seed = gr.Number(
|
| 1484 |
label="Seed (-1 for random)",
|
| 1485 |
value=DEFAULT_SEED,
|
| 1486 |
precision=0,
|
| 1487 |
)
|
| 1488 |
+
|
| 1489 |
+
with gr.Accordion("Mask behavior", open=False):
|
| 1490 |
+
gr.Markdown("Use **Keep/Dilate** for full objects. Use **Erode** only for small face-area masks where preserving facial detail matters.")
|
| 1491 |
+
mask_dilation_iter = gr.Slider(
|
| 1492 |
+
label="Mask dilation iterations",
|
| 1493 |
+
minimum=0,
|
| 1494 |
+
maximum=20,
|
| 1495 |
+
value=DEFAULT_MASK_DILATION_ITER,
|
| 1496 |
+
step=1,
|
| 1497 |
+
info="Higher values cover object edges better. Lower values preserve more face detail.",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1498 |
)
|
| 1499 |
+
with gr.Row():
|
| 1500 |
+
mask_refine_mode = gr.Dropdown(
|
| 1501 |
+
label="Mask refine mode",
|
| 1502 |
+
choices=MASK_REFINE_MODE_CHOICES,
|
| 1503 |
+
value=DEFAULT_MASK_REFINE_MODE,
|
| 1504 |
+
info="Keep is safest. Erode shrinks masks. Dilate expands masks.",
|
| 1505 |
+
)
|
| 1506 |
+
mask_refine_iterations = gr.Slider(
|
| 1507 |
+
label="Mask refine iterations",
|
| 1508 |
+
minimum=0,
|
| 1509 |
+
maximum=4,
|
| 1510 |
+
value=DEFAULT_MASK_REFINE_ITERATIONS,
|
| 1511 |
+
step=1,
|
| 1512 |
+
)
|
| 1513 |
+
mask_feather_px = gr.Slider(
|
| 1514 |
+
label="Mask feather (px)",
|
| 1515 |
+
minimum=0,
|
| 1516 |
+
maximum=16,
|
| 1517 |
+
value=DEFAULT_MASK_FEATHER_PX,
|
| 1518 |
+
step=1,
|
| 1519 |
+
info="Softens mask edges for DiffuEraser. Use 0 for the original hard mask behavior.",
|
| 1520 |
)
|
| 1521 |
+
|
| 1522 |
+
with gr.Accordion("Face watermark tuning", open=False):
|
| 1523 |
+
gr.Markdown("Only for small masks over facial details. This mode trades complete removal for preserving more face detail.")
|
| 1524 |
+
face_watermark_mode = gr.Dropdown(
|
| 1525 |
+
label="Face watermark mode",
|
| 1526 |
+
choices=FACE_WATERMARK_MODE_CHOICES,
|
| 1527 |
+
value=DEFAULT_FACE_WATERMARK_MODE,
|
| 1528 |
+
info="Off keeps the normal result. Original blend preserves more original face detail. ProPainter blend can reduce DiffuEraser hallucination.",
|
| 1529 |
)
|
| 1530 |
+
face_correction_strength = gr.Slider(
|
| 1531 |
+
label="Face watermark correction strength",
|
| 1532 |
+
minimum=0.0,
|
| 1533 |
+
maximum=1.0,
|
| 1534 |
+
value=DEFAULT_FACE_CORRECTION_STRENGTH,
|
| 1535 |
+
step=0.05,
|
| 1536 |
+
info="Higher removes more of the masked mark. Lower preserves more original/prior face detail.",
|
| 1537 |
+
)
|
| 1538 |
+
prompt = gr.Textbox(
|
| 1539 |
+
label="Prompt",
|
| 1540 |
+
value=DEFAULT_PROMPT,
|
| 1541 |
+
lines=2,
|
| 1542 |
+
placeholder="Optional. Leave blank for the original DiffuEraser behavior.",
|
| 1543 |
+
)
|
| 1544 |
+
negative_prompt = gr.Textbox(
|
| 1545 |
+
label="Negative prompt",
|
| 1546 |
+
value=DEFAULT_NEGATIVE_PROMPT,
|
| 1547 |
+
lines=2,
|
| 1548 |
+
placeholder="Optional. Mainly affects results when guidance scale is above 1.",
|
| 1549 |
+
)
|
| 1550 |
+
guidance_scale = gr.Slider(
|
| 1551 |
+
label="Guidance scale (-1 uses checkpoint default)",
|
| 1552 |
+
minimum=-1,
|
| 1553 |
+
maximum=12,
|
| 1554 |
+
value=DEFAULT_GUIDANCE_SCALE,
|
| 1555 |
+
step=0.5,
|
| 1556 |
+
info="Use -1 to keep each PCM checkpoint's default guidance scale.",
|
| 1557 |
+
)
|
| 1558 |
+
|
| 1559 |
+
with gr.Accordion("ProPainter and runtime", open=False):
|
| 1560 |
+
with gr.Row(visible=IS_ZEROGPU_SPACE):
|
| 1561 |
+
zero_gpu_duration_mode = gr.Dropdown(
|
| 1562 |
+
label="ZeroGPU duration mode",
|
| 1563 |
+
choices=ZERO_GPU_DURATION_MODE_CHOICES,
|
| 1564 |
+
value=DEFAULT_ZERO_GPU_DURATION_MODE,
|
| 1565 |
+
info="Auto estimates the reservation from the current settings. Normal GPU Spaces ignore this setting.",
|
| 1566 |
+
)
|
| 1567 |
+
zero_gpu_duration = gr.Slider(
|
| 1568 |
+
label="Manual ZeroGPU reserved duration (seconds)",
|
| 1569 |
+
minimum=30,
|
| 1570 |
+
maximum=300,
|
| 1571 |
+
value=DEFAULT_ZERO_GPU_DURATION,
|
| 1572 |
+
step=30,
|
| 1573 |
+
info="Used only when ZeroGPU duration mode is Manual.",
|
| 1574 |
+
)
|
| 1575 |
+
with gr.Row():
|
| 1576 |
+
subvideo_length = gr.Slider(
|
| 1577 |
+
label="ProPainter subvideo length",
|
| 1578 |
+
minimum=10,
|
| 1579 |
+
maximum=100,
|
| 1580 |
+
value=DEFAULT_SUBVIDEO_LENGTH,
|
| 1581 |
+
step=5,
|
| 1582 |
+
)
|
| 1583 |
+
ref_stride = gr.Slider(
|
| 1584 |
+
label="ProPainter reference stride",
|
| 1585 |
+
minimum=1,
|
| 1586 |
+
maximum=20,
|
| 1587 |
+
value=DEFAULT_REF_STRIDE,
|
| 1588 |
+
step=1,
|
| 1589 |
+
)
|
| 1590 |
+
neighbor_length = gr.Slider(
|
| 1591 |
+
label="ProPainter neighbor length",
|
| 1592 |
+
minimum=2,
|
| 1593 |
+
maximum=30,
|
| 1594 |
+
value=DEFAULT_NEIGHBOR_LENGTH,
|
| 1595 |
+
step=2,
|
| 1596 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1597 |
|
| 1598 |
with gr.Row():
|
| 1599 |
submit_btn = gr.Button("Run", variant="primary")
|
|
|
|
| 1603 |
video_result = gr.Video(label="Result", buttons=["download"])
|
| 1604 |
with gr.Accordion("Debug outputs", open=False):
|
| 1605 |
trimmed_video_result = gr.Video(label="Trimmed input video", interactive=False)
|
| 1606 |
+
refined_mask_result = gr.Video(label="Refined mask preview", interactive=False)
|
| 1607 |
priori_result = gr.Video(label="ProPainter priori video", interactive=False)
|
| 1608 |
+
raw_diffueraser_result = gr.Video(label="Raw DiffuEraser result", interactive=False)
|
| 1609 |
debug_log_file = gr.File(label="Debug log file", interactive=False)
|
| 1610 |
gr.Examples(
|
| 1611 |
examples=[
|
|
|
|
| 1623 |
max_duration,
|
| 1624 |
max_img_size,
|
| 1625 |
mask_dilation_iter,
|
| 1626 |
+
mask_refine_mode,
|
| 1627 |
+
mask_refine_iterations,
|
| 1628 |
+
mask_feather_px,
|
| 1629 |
subvideo_length,
|
| 1630 |
ref_stride,
|
| 1631 |
neighbor_length,
|
|
|
|
| 1637 |
guidance_scale,
|
| 1638 |
zero_gpu_duration_mode,
|
| 1639 |
zero_gpu_duration,
|
| 1640 |
+
face_watermark_mode,
|
| 1641 |
+
face_correction_strength,
|
| 1642 |
prompt,
|
| 1643 |
negative_prompt,
|
| 1644 |
],
|
| 1645 |
outputs=[
|
| 1646 |
+
settings_summary,
|
| 1647 |
max_duration,
|
| 1648 |
max_img_size,
|
| 1649 |
mask_dilation_iter,
|
| 1650 |
+
mask_refine_mode,
|
| 1651 |
+
mask_refine_iterations,
|
| 1652 |
+
mask_feather_px,
|
| 1653 |
subvideo_length,
|
| 1654 |
ref_stride,
|
| 1655 |
neighbor_length,
|
|
|
|
| 1661 |
guidance_scale,
|
| 1662 |
zero_gpu_duration_mode,
|
| 1663 |
zero_gpu_duration,
|
| 1664 |
+
face_watermark_mode,
|
| 1665 |
+
face_correction_strength,
|
| 1666 |
prompt,
|
| 1667 |
negative_prompt,
|
| 1668 |
],
|
|
|
|
| 1671 |
clear_btn.click(
|
| 1672 |
fn=clear_inputs,
|
| 1673 |
inputs=None,
|
| 1674 |
+
outputs=[input_video, input_mask, video_result, trimmed_video_result, refined_mask_result, priori_result, raw_diffueraser_result, debug_log_file],
|
| 1675 |
)
|
| 1676 |
|
| 1677 |
submit_btn.click(
|
|
|
|
| 1681 |
input_mask,
|
| 1682 |
max_duration,
|
| 1683 |
mask_dilation_iter,
|
| 1684 |
+
mask_refine_mode,
|
| 1685 |
+
mask_refine_iterations,
|
| 1686 |
+
mask_feather_px,
|
| 1687 |
max_img_size,
|
| 1688 |
ref_stride,
|
| 1689 |
neighbor_length,
|
|
|
|
| 1696 |
guidance_scale,
|
| 1697 |
zero_gpu_duration_mode,
|
| 1698 |
zero_gpu_duration,
|
| 1699 |
+
face_watermark_mode,
|
| 1700 |
+
face_correction_strength,
|
| 1701 |
prompt,
|
| 1702 |
negative_prompt,
|
| 1703 |
],
|
| 1704 |
+
outputs=[video_result, trimmed_video_result, refined_mask_result, priori_result, raw_diffueraser_result, debug_log_file],
|
| 1705 |
)
|
| 1706 |
|
| 1707 |
demo.queue().launch(show_error=True, ssr_mode=False)
|