| 215 | |
| 216 | |
| 217 | def generate_alphas( |
| 218 | clips, |
| 219 | device=None, |
| 220 | *, |
| 221 | on_clip_start: Callable[[str, int], None] | None = None, |
| 222 | ): |
| 223 | clips_to_process = [c for c in clips if c.alpha_asset is None] |
| 224 | |
| 225 | if not clips_to_process: |
| 226 | logger.info("All clips have valid Alpha assets. No generation needed.") |
| 227 | return |
| 228 | |
| 229 | logger.info(f"Found {len(clips_to_process)} clips missing Alpha.") |
| 230 | |
| 231 | if device is None: |
| 232 | device = resolve_device() |
| 233 | |
| 234 | try: |
| 235 | processor = get_gvm_processor(device=device) |
| 236 | except ImportError as e: |
| 237 | logger.error(f"GVM Import Error: {e}") |
| 238 | logger.error("Skipping GVM generation. Please install GVM requirements if you wish to use this feature.") |
| 239 | return |
| 240 | except Exception as e: |
| 241 | logger.error(f"GVM Initialization Error: {e}") |
| 242 | return |
| 243 | |
| 244 | for clip in clips_to_process: |
| 245 | logger.info(f"Generating Alpha for: {clip.name}") |
| 246 | if on_clip_start: |
| 247 | on_clip_start(clip.name, len(clips_to_process)) |
| 248 | |
| 249 | alpha_output_dir = os.path.join(clip.root_path, "AlphaHint") |
| 250 | if os.path.exists(alpha_output_dir): |
| 251 | shutil.rmtree(alpha_output_dir) |
| 252 | os.makedirs(alpha_output_dir, exist_ok=True) |
| 253 | |
| 254 | try: |
| 255 | processor.process_sequence( |
| 256 | input_path=clip.input_asset.path, |
| 257 | output_dir=None, |
| 258 | num_frames_per_batch=1, |
| 259 | decode_chunk_size=1, |
| 260 | denoise_steps=1, |
| 261 | mode="matte", |
| 262 | write_video=False, |
| 263 | direct_output_dir=alpha_output_dir, |
| 264 | ) |
| 265 | |
| 266 | # Post-Process: Naming Convention |
| 267 | generated_files = sorted([f for f in os.listdir(alpha_output_dir) if f.endswith(".png")]) |
| 268 | |
| 269 | if not generated_files: |
| 270 | logger.error(f"GVM finished but no PNGs found in {alpha_output_dir}") |
| 271 | continue |
| 272 | |
| 273 | if clip.input_asset.type == "sequence": |
| 274 | in_files = sorted([f for f in os.listdir(clip.input_asset.path) if is_image_file(f)]) |