Initial release — OpenMontage: the first open-source agentic video production system

11 production pipelines, 47 tools, 124 agent skills.
Supports cloud APIs (fal.ai, OpenAI, ElevenLabs, Suno, HeyGen, Runway) and
free local providers (diffusers, Piper TTS, WAN 2.1, Hunyuan, CogVideo).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
calesthio
2026-03-29 08:25:17 -07:00
commit a3e735cc7a
1147 changed files with 240221 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
"""Enhancement tools for image and video quality improvement."""
+161
View File
@@ -0,0 +1,161 @@
"""Background removal tool wrapping rembg.
Removes backgrounds from images using the rembg library (U2Net models).
Outputs transparent PNGs or composites onto a custom background color.
Supports local execution via rembg and optionally cloud APIs.
"""
from __future__ import annotations
import time
from pathlib import Path
from typing import Any
from tools.base_tool import (
BaseTool,
Determinism,
ExecutionMode,
ResourceProfile,
ToolResult,
ToolRuntime,
ToolStability,
ToolStatus,
ToolTier,
)
class BgRemove(BaseTool):
name = "bg_remove"
version = "0.1.0"
tier = ToolTier.ENHANCE
capability = "enhancement"
provider = "rembg"
stability = ToolStability.EXPERIMENTAL
execution_mode = ExecutionMode.SYNC
determinism = Determinism.DETERMINISTIC
runtime = ToolRuntime.HYBRID
dependencies = ["python:rembg", "python:PIL"]
install_instructions = (
"pip install rembg # CPU mode\n"
"pip install rembg[gpu] # GPU mode (requires CUDA + onnxruntime-gpu)"
)
agent_skills = ["ffmpeg"]
capabilities = [
"background_removal",
"alpha_matte",
"batch_processing",
"custom_background",
]
input_schema = {
"type": "object",
"required": ["input_path"],
"properties": {
"input_path": {
"type": "string",
"description": "Path to image or video frame",
},
"output_path": {
"type": "string",
"description": "Output path; defaults to {stem}_nobg.png",
},
"model": {
"type": "string",
"enum": ["u2net", "u2net_human_seg", "isnet-general-use"],
"default": "u2net",
},
"bg_color": {
"type": "string",
"description": "Replacement background color hex (e.g. #00FF00). Transparent if not set.",
},
"alpha_matting": {
"type": "boolean",
"default": False,
"description": "Use alpha matting for finer edges",
},
},
}
resource_profile = ResourceProfile(
cpu_cores=2, ram_mb=2048, vram_mb=0, disk_mb=500
)
idempotency_key_fields = ["input_path", "model", "bg_color", "alpha_matting"]
side_effects = ["writes background-removed image to output_path"]
user_visible_verification = [
"Inspect output for clean edges around the subject",
"Verify transparency or background color is applied correctly",
]
def get_status(self) -> ToolStatus:
try:
import rembg # noqa: F401
return ToolStatus.AVAILABLE
except ImportError:
return ToolStatus.UNAVAILABLE
def execute(self, inputs: dict[str, Any]) -> ToolResult:
input_path = Path(inputs["input_path"])
if not input_path.exists():
return ToolResult(success=False, error=f"Input not found: {input_path}")
output_path = Path(
inputs.get("output_path", str(input_path.with_stem(f"{input_path.stem}_nobg").with_suffix(".png")))
)
model_name = inputs.get("model", "u2net")
bg_color = inputs.get("bg_color")
alpha_matting = inputs.get("alpha_matting", False)
try:
import rembg
except ImportError:
return ToolResult(
success=False,
error="rembg is not installed. Run: pip install rembg",
)
try:
from PIL import Image
except ImportError:
return ToolResult(
success=False,
error="Pillow is not installed. Run: pip install Pillow",
)
start = time.time()
input_image = Image.open(input_path)
result_image = rembg.remove(
input_image,
model_name=model_name,
alpha_matting=alpha_matting,
)
# Composite onto a colored background if requested
if bg_color:
hex_color = bg_color.lstrip("#")
r, g, b = int(hex_color[0:2], 16), int(hex_color[2:4], 16), int(hex_color[4:6], 16)
background = Image.new("RGBA", result_image.size, (r, g, b, 255))
background.paste(result_image, mask=result_image.split()[3])
result_image = background.convert("RGB")
output_path.parent.mkdir(parents=True, exist_ok=True)
result_image.save(str(output_path))
elapsed = time.time() - start
return ToolResult(
success=True,
data={
"input": str(input_path),
"output": str(output_path),
"model": model_name,
"alpha_matting": alpha_matting,
"bg_color": bg_color,
},
artifacts=[str(output_path)],
duration_seconds=round(elapsed, 2),
)
+212
View File
@@ -0,0 +1,212 @@
"""Color grading tool wrapping FFmpeg LUT and filter chains.
Applies cinematic color grading profiles to video. Supports both
built-in profile presets and external .cube LUT files.
"""
from __future__ import annotations
import time
from pathlib import Path
from typing import Any
from tools.base_tool import (
BaseTool,
Determinism,
ExecutionMode,
ResourceProfile,
ToolResult,
ToolStability,
ToolTier,
)
# Built-in grading profiles using FFmpeg colorbalance/curves/eq filters
PROFILES = {
"cinematic_warm": {
"description": "Warm cinematic look with lifted shadows and orange highlights",
"vf": (
"colorbalance=rs=0.08:gs=0.02:bs=-0.05:rh=0.06:gh=0.02:bh=-0.04,"
"curves=all='0/0.03 0.25/0.22 0.5/0.50 0.75/0.78 1/0.97',"
"eq=contrast=1.05:saturation=1.1"
),
},
"cinematic_cool": {
"description": "Cool teal-and-orange cinematic grade",
"vf": (
"colorbalance=rs=-0.02:gs=-0.03:bs=0.08:rh=0.06:gh=-0.02:bh=-0.06,"
"curves=all='0/0.02 0.25/0.20 0.5/0.48 0.75/0.78 1/0.98',"
"eq=contrast=1.08:saturation=1.05"
),
},
"moody_dark": {
"description": "Crushed blacks, desaturated midtones, dark atmosphere",
"vf": (
"curves=all='0/0.05 0.15/0.12 0.5/0.45 0.85/0.82 1/0.95',"
"eq=contrast=1.12:saturation=0.8:brightness=-0.03"
),
},
"bright_clean": {
"description": "Bright, clean look with lifted shadows and vivid color",
"vf": (
"curves=all='0/0.05 0.25/0.30 0.5/0.55 0.75/0.80 1/1.0',"
"eq=contrast=1.0:saturation=1.15:brightness=0.02"
),
},
"vintage_film": {
"description": "Faded film look with grain texture and warm tint",
"vf": (
"colorbalance=rs=0.06:gs=0.03:bs=-0.03:ms=0.03:mh=-0.02,"
"curves=all='0/0.06 0.25/0.25 0.5/0.50 0.75/0.74 1/0.94',"
"eq=saturation=0.85:contrast=0.95"
),
},
"high_contrast": {
"description": "Punchy high-contrast grade for dynamic content",
"vf": (
"curves=all='0/0 0.20/0.12 0.5/0.50 0.80/0.88 1/1',"
"eq=contrast=1.2:saturation=1.1"
),
},
"neutral": {
"description": "Minimal correction — normalize levels and light contrast",
"vf": "eq=contrast=1.02:saturation=1.02:brightness=0.01",
},
}
class ColorGrade(BaseTool):
name = "color_grade"
version = "0.1.0"
tier = ToolTier.CORE
capability = "enhancement"
provider = "ffmpeg"
stability = ToolStability.EXPERIMENTAL
execution_mode = ExecutionMode.SYNC
determinism = Determinism.DETERMINISTIC
dependencies = ["cmd:ffmpeg"]
install_instructions = "Install FFmpeg: https://ffmpeg.org/download.html"
agent_skills = ["ffmpeg"]
capabilities = [
"grade_preset",
"grade_lut",
"grade_custom",
]
input_schema = {
"type": "object",
"required": ["input_path"],
"properties": {
"input_path": {"type": "string"},
"output_path": {"type": "string"},
"profile": {
"type": "string",
"enum": list(PROFILES.keys()),
"default": "cinematic_warm",
},
"lut_path": {
"type": "string",
"description": "Path to external .cube LUT file",
},
"intensity": {
"type": "number",
"minimum": 0.0,
"maximum": 1.0,
"default": 1.0,
"description": "Blend intensity: 0 = original, 1 = full grade",
},
"custom_vf": {"type": "string"},
"codec": {"type": "string", "default": "libx264"},
"crf": {"type": "integer", "default": 20},
},
}
resource_profile = ResourceProfile(cpu_cores=2, ram_mb=1024, vram_mb=0, disk_mb=2000)
idempotency_key_fields = ["input_path", "profile", "lut_path", "intensity"]
side_effects = ["writes graded video to output_path"]
user_visible_verification = [
"Compare graded output with original for color accuracy",
"Verify skin tones look natural, not oversaturated",
]
def execute(self, inputs: dict[str, Any]) -> ToolResult:
input_path = Path(inputs["input_path"])
if not input_path.exists():
return ToolResult(success=False, error=f"Input not found: {input_path}")
output_path = Path(
inputs.get("output_path", str(input_path.with_stem(f"{input_path.stem}_graded")))
)
codec = inputs.get("codec", "libx264")
crf = inputs.get("crf", 20)
vf = self._build_filter(inputs)
if not vf:
return ToolResult(success=False, error="No profile, lut_path, or custom_vf specified")
start = time.time()
cmd = [
"ffmpeg", "-y",
"-i", str(input_path),
"-vf", vf,
"-c:v", codec, "-crf", str(crf),
"-c:a", "copy",
str(output_path),
]
try:
self.run_command(cmd)
except Exception as e:
return ToolResult(success=False, error=f"FFmpeg failed: {e}")
elapsed = time.time() - start
return ToolResult(
success=True,
data={
"input": str(input_path),
"output": str(output_path),
"profile": inputs.get("profile"),
"lut": inputs.get("lut_path"),
"intensity": inputs.get("intensity", 1.0),
"filter": vf,
},
artifacts=[str(output_path)],
duration_seconds=round(elapsed, 2),
)
def _build_filter(self, inputs: dict[str, Any]) -> str:
if "custom_vf" in inputs:
return inputs["custom_vf"]
lut_path = inputs.get("lut_path")
if lut_path and Path(lut_path).exists():
safe_path = str(Path(lut_path).resolve()).replace("\\", "/").replace(":", "\\:")
return f"lut3d='{safe_path}'"
profile_name = inputs.get("profile", "cinematic_warm")
profile = PROFILES.get(profile_name)
if not profile:
return ""
vf = profile["vf"]
# Apply intensity blending if < 1.0
intensity = inputs.get("intensity", 1.0)
if 0 < intensity < 1.0:
# Use split + overlay approach: blend graded with original
vf = (
f"split[original][tograde];"
f"[tograde]{vf}[graded];"
f"[original][graded]blend=all_mode=normal:all_opacity={intensity}"
)
return vf
@staticmethod
def list_profiles() -> dict[str, str]:
"""Return available profiles and their descriptions."""
return {name: p["description"] for name, p in PROFILES.items()}
+193
View File
@@ -0,0 +1,193 @@
"""Face enhancement tool wrapping FFmpeg filters.
Applies skin smoothing, sharpening, and lighting correction presets
to talking-head footage. All presets are FFmpeg filter chains — no GPU
or external models required.
"""
from __future__ import annotations
import time
from pathlib import Path
from typing import Any
from tools.base_tool import (
BaseTool,
Determinism,
ExecutionMode,
ResourceProfile,
ToolResult,
ToolStability,
ToolTier,
)
# Named presets mapping to FFmpeg filter chains
PRESETS = {
"soft_skin": {
"description": "Gentle skin smoothing while preserving edges",
"vf": "smartblur=lr=1.0:ls=-0.5:lt=-3.0:cr=0.5:cs=-0.5:ct=-3.0",
},
"sharpen": {
"description": "Edge sharpening for crisp detail",
"vf": "unsharp=5:5:1.0:5:5:0.0",
},
"sharpen_light": {
"description": "Subtle sharpening for soft cameras",
"vf": "unsharp=3:3:0.5:3:3:0.0",
},
"brighten": {
"description": "Lift shadows and midtones for poorly lit footage",
"vf": "curves=all='0/0 0.25/0.35 0.5/0.55 0.75/0.8 1/1'",
},
"contrast_boost": {
"description": "Add punch with an S-curve contrast adjustment",
"vf": "curves=all='0/0 0.25/0.20 0.5/0.5 0.75/0.80 1/1'",
},
"warm": {
"description": "Warm skin tones — slight orange shift",
"vf": "colorbalance=rs=0.05:gs=0.0:bs=-0.05:rm=0.05:gm=0.0:bm=-0.03",
},
"cool": {
"description": "Cool tones — slight blue shift",
"vf": "colorbalance=rs=-0.03:gs=0.0:bs=0.05:rm=-0.02:gm=0.0:bm=0.03",
},
"denoise": {
"description": "Temporal noise reduction for grainy footage",
"vf": "hqdn3d=4:3:6:4",
},
"talking_head_standard": {
"description": "Combined preset: skin smoothing + sharpen edges + warm skin tones",
"vf": (
"smartblur=lr=1.0:ls=-0.5:lt=-3.0:cr=0.5:cs=-0.5:ct=-3.0,"
"unsharp=5:5:0.6:5:5:0.0,"
"colorbalance=rs=0.06:gs=0.01:bs=-0.04:rm=0.04:gm=0.01:bm=-0.03"
),
},
}
class FaceEnhance(BaseTool):
name = "face_enhance"
version = "0.1.0"
tier = ToolTier.CORE
capability = "enhancement"
provider = "ffmpeg"
stability = ToolStability.EXPERIMENTAL
execution_mode = ExecutionMode.SYNC
determinism = Determinism.DETERMINISTIC
dependencies = ["cmd:ffmpeg"]
install_instructions = "Install FFmpeg: https://ffmpeg.org/download.html"
agent_skills = ["ffmpeg"]
capabilities = [
"skin_smoothing",
"sharpening",
"lighting_correction",
"color_balance",
"denoise",
"preset_chain",
]
input_schema = {
"type": "object",
"required": ["input_path"],
"properties": {
"input_path": {"type": "string"},
"output_path": {"type": "string"},
"preset": {
"type": "string",
"enum": list(PRESETS.keys()),
"default": "talking_head_standard",
},
"presets": {
"type": "array",
"items": {"type": "string"},
"description": "Apply multiple presets in sequence",
},
"custom_vf": {
"type": "string",
"description": "Custom FFmpeg video filter string (advanced)",
},
"codec": {"type": "string", "default": "libx264"},
"crf": {"type": "integer", "default": 20},
},
}
resource_profile = ResourceProfile(cpu_cores=2, ram_mb=1024, vram_mb=0, disk_mb=2000)
idempotency_key_fields = ["input_path", "preset", "presets", "custom_vf"]
side_effects = ["writes enhanced video to output_path"]
user_visible_verification = [
"Compare enhanced output with original side-by-side",
"Verify skin texture is natural, not plastic",
]
def execute(self, inputs: dict[str, Any]) -> ToolResult:
input_path = Path(inputs["input_path"])
if not input_path.exists():
return ToolResult(success=False, error=f"Input not found: {input_path}")
output_path = Path(
inputs.get("output_path", str(input_path.with_stem(f"{input_path.stem}_enhanced")))
)
codec = inputs.get("codec", "libx264")
crf = inputs.get("crf", 20)
# Build filter chain
vf = self._build_filter(inputs)
if not vf:
return ToolResult(success=False, error="No preset, presets, or custom_vf specified")
start = time.time()
cmd = [
"ffmpeg", "-y",
"-i", str(input_path),
"-vf", vf,
"-c:v", codec, "-crf", str(crf),
"-c:a", "copy",
str(output_path),
]
try:
self.run_command(cmd)
except Exception as e:
return ToolResult(success=False, error=f"FFmpeg failed: {e}")
elapsed = time.time() - start
return ToolResult(
success=True,
data={
"input": str(input_path),
"output": str(output_path),
"filter": vf,
"preset": inputs.get("preset"),
},
artifacts=[str(output_path)],
duration_seconds=round(elapsed, 2),
)
def _build_filter(self, inputs: dict[str, Any]) -> str:
if "custom_vf" in inputs:
return inputs["custom_vf"]
if "presets" in inputs:
chains = []
for name in inputs["presets"]:
if name not in PRESETS:
continue
chains.append(PRESETS[name]["vf"])
return ",".join(chains)
preset_name = inputs.get("preset", "talking_head_standard")
preset = PRESETS.get(preset_name)
if preset:
return preset["vf"]
return ""
@staticmethod
def list_presets() -> dict[str, str]:
"""Return available presets and their descriptions."""
return {name: p["description"] for name, p in PRESETS.items()}
+230
View File
@@ -0,0 +1,230 @@
"""Face restoration tool wrapping CodeFormer / GFPGAN.
Restores degraded or low-quality faces in images and video frames.
Fixes blur, compression artifacts, and low resolution specifically on
face regions while preserving the rest of the image.
"""
from __future__ import annotations
import time
from pathlib import Path
from typing import Any
from tools.base_tool import (
BaseTool,
Determinism,
ExecutionMode,
ResourceProfile,
ToolResult,
ToolRuntime,
ToolStability,
ToolStatus,
ToolTier,
)
class FaceRestore(BaseTool):
name = "face_restore"
version = "0.1.0"
tier = ToolTier.ENHANCE
capability = "enhancement"
provider = "codeformer"
stability = ToolStability.EXPERIMENTAL
execution_mode = ExecutionMode.SYNC
determinism = Determinism.DETERMINISTIC
runtime = ToolRuntime.LOCAL_GPU
dependencies = ["python:gfpgan", "python:torch"]
install_instructions = (
"pip install gfpgan # Includes CodeFormer support. Requires PyTorch."
)
agent_skills = ["ffmpeg"]
fallback = None
capabilities = [
"face_restoration",
"face_detection",
"quality_enhancement",
]
input_schema = {
"type": "object",
"required": ["input_path"],
"properties": {
"input_path": {
"type": "string",
"description": "Path to image or video frame",
},
"output_path": {
"type": "string",
"description": "Output path (defaults to {stem}_restored.{ext})",
},
"model": {
"type": "string",
"enum": ["CodeFormer", "GFPGAN"],
"default": "CodeFormer",
"description": "Restoration model to use",
},
"fidelity": {
"type": "number",
"default": 0.5,
"description": (
"0 = max quality, 1 = max fidelity to input (CodeFormer only)"
),
},
"upscale": {
"type": "integer",
"default": 2,
"description": "Face upscale factor",
},
"bg_upsampler": {
"type": "boolean",
"default": False,
"description": "Also upscale background with Real-ESRGAN",
},
},
}
resource_profile = ResourceProfile(
cpu_cores=2, ram_mb=2048, vram_mb=2048, disk_mb=1000
)
idempotency_key_fields = ["input_path", "model", "fidelity", "upscale"]
side_effects = ["writes restored image to output_path"]
user_visible_verification = [
"Compare restored face with original for natural appearance",
"Verify face identity is preserved after restoration",
]
def get_status(self) -> ToolStatus:
try:
import gfpgan # noqa: F401
return ToolStatus.AVAILABLE
except ImportError:
return ToolStatus.UNAVAILABLE
def execute(self, inputs: dict[str, Any]) -> ToolResult:
input_path = Path(inputs["input_path"])
if not input_path.exists():
return ToolResult(success=False, error=f"Input not found: {input_path}")
output_path = Path(
inputs.get(
"output_path",
str(input_path.with_stem(f"{input_path.stem}_restored")),
)
)
model_name = inputs.get("model", "CodeFormer")
fidelity = inputs.get("fidelity", 0.5)
upscale = inputs.get("upscale", 2)
bg_upsampler_flag = inputs.get("bg_upsampler", False)
try:
import cv2
from gfpgan import GFPGANer
except ImportError as e:
return ToolResult(
success=False,
error=f"Missing dependency: {e}. Run: pip install gfpgan",
)
start = time.time()
# Optional background upsampler
bg_upsampler = None
if bg_upsampler_flag:
try:
from basicsr.archs.rrdbnet_arch import RRDBNet
from realesrgan import RealESRGANer
realesrgan_model = RRDBNet(
num_in_ch=3, num_out_ch=3, num_feat=64,
num_block=23, num_grow_ch=32, scale=2,
)
bg_upsampler = RealESRGANer(
scale=2,
model_path=(
"https://github.com/xinntao/Real-ESRGAN/releases/download/"
"v0.2.1/RealESRGAN_x2plus.pth"
),
model=realesrgan_model,
tile=400,
tile_pad=10,
pre_pad=0,
half=True,
)
except ImportError:
bg_upsampler = None
# Select model path based on model choice
if model_name == "CodeFormer":
model_path = (
"https://github.com/sczhou/CodeFormer/releases/download/"
"v0.1.0/codeformer.pth"
)
arch = "CodeFormer"
else:
model_path = (
"https://github.com/TencentARC/GFPGAN/releases/download/"
"v1.3.0/GFPGANv1.3.pth"
)
arch = "clean"
# Instantiate restorer
try:
restorer = GFPGANer(
model_path=model_path,
upscale=upscale,
arch=arch,
bg_upsampler=bg_upsampler,
)
except Exception as e:
return ToolResult(
success=False, error=f"Failed to load {model_name} model: {e}"
)
# Read input image
input_img = cv2.imread(str(input_path), cv2.IMREAD_COLOR)
if input_img is None:
return ToolResult(
success=False, error=f"Failed to read image: {input_path}"
)
# Run restoration
try:
_, restored_faces, restored_img = restorer.enhance(
input_img,
has_aligned=False,
only_center_face=False,
paste_back=True,
weight=fidelity if model_name == "CodeFormer" else None,
)
except Exception as e:
return ToolResult(success=False, error=f"Restoration failed: {e}")
if restored_img is None:
return ToolResult(
success=False, error="Restoration produced no output"
)
# Save restored output
output_path.parent.mkdir(parents=True, exist_ok=True)
cv2.imwrite(str(output_path), restored_img)
elapsed = time.time() - start
faces_detected = len(restored_faces) if restored_faces else 0
return ToolResult(
success=True,
data={
"input": str(input_path),
"output": str(output_path),
"model": model_name,
"faces_detected": faces_detected,
"upscale": upscale,
"fidelity": fidelity if model_name == "CodeFormer" else None,
"bg_upsampler": bg_upsampler_flag,
},
artifacts=[str(output_path)],
duration_seconds=round(elapsed, 2),
)
+339
View File
@@ -0,0 +1,339 @@
"""Image and video upscaling tool using Real-ESRGAN.
Takes low-resolution images or video and produces higher-resolution output
(2x or 4x). For video, frames are extracted via FFmpeg, upscaled individually,
and reassembled into the output file.
"""
from __future__ import annotations
import shutil
import tempfile
import time
from pathlib import Path
from typing import Any
from tools.base_tool import (
BaseTool,
Determinism,
ExecutionMode,
ResourceProfile,
ToolResult,
ToolRuntime,
ToolStability,
ToolStatus,
ToolTier,
)
VIDEO_EXTENSIONS = {".mp4", ".mov", ".avi"}
MODELS = {
"RealESRGAN_x4plus": {
"description": "General-purpose photo/video upscaler (default)",
"scale": 4,
},
"RealESRGAN_x4plus_anime_6B": {
"description": "Optimised for anime/illustration content",
"scale": 4,
},
"RealESRNet_x4plus": {
"description": "Lighter network, faster but lower quality",
"scale": 4,
},
}
class Upscale(BaseTool):
name = "upscale"
version = "0.1.0"
tier = ToolTier.ENHANCE
capability = "enhancement"
provider = "realesrgan"
stability = ToolStability.EXPERIMENTAL
execution_mode = ExecutionMode.SYNC
determinism = Determinism.DETERMINISTIC
runtime = ToolRuntime.LOCAL_GPU
dependencies = ["python:realesrgan", "python:torch", "cmd:ffmpeg"]
install_instructions = "pip install realesrgan # Requires PyTorch with CUDA"
agent_skills = ["ffmpeg"]
capabilities = [
"image_upscale",
"video_upscale",
"face_aware_upscale",
]
input_schema = {
"type": "object",
"required": ["input_path"],
"properties": {
"input_path": {"type": "string"},
"output_path": {"type": "string"},
"scale": {
"type": "integer",
"enum": [2, 4],
"default": 4,
},
"model": {
"type": "string",
"enum": list(MODELS.keys()),
"default": "RealESRGAN_x4plus",
},
"face_enhance": {
"type": "boolean",
"default": False,
"description": "Use GFPGAN for face regions",
},
"denoise_strength": {
"type": "number",
"minimum": 0.0,
"maximum": 1.0,
"default": 0.5,
"description": "Denoising strength (0 = no denoise, 1 = full)",
},
},
}
resource_profile = ResourceProfile(cpu_cores=2, ram_mb=4096, vram_mb=2048, disk_mb=2000)
idempotency_key_fields = ["input_path", "scale", "model", "face_enhance", "denoise_strength"]
side_effects = ["writes upscaled file to output_path"]
user_visible_verification = [
"Compare upscaled output with original for detail and artifact quality",
"Verify faces look natural if face_enhance was enabled",
]
# ------------------------------------------------------------------
# Status
# ------------------------------------------------------------------
def get_status(self) -> ToolStatus:
try:
import realesrgan # noqa: F401
return ToolStatus.AVAILABLE
except ImportError:
return ToolStatus.UNAVAILABLE
# ------------------------------------------------------------------
# Execution
# ------------------------------------------------------------------
def execute(self, inputs: dict[str, Any]) -> ToolResult:
input_path = Path(inputs["input_path"])
if not input_path.exists():
return ToolResult(success=False, error=f"Input not found: {input_path}")
is_video = input_path.suffix.lower() in VIDEO_EXTENSIONS
default_output = str(input_path.with_stem(f"{input_path.stem}_upscaled"))
output_path = Path(inputs.get("output_path", default_output))
output_path.parent.mkdir(parents=True, exist_ok=True)
scale = inputs.get("scale", 4)
model_name = inputs.get("model", "RealESRGAN_x4plus")
face_enhance = inputs.get("face_enhance", False)
denoise_strength = inputs.get("denoise_strength", 0.5)
start = time.time()
try:
if is_video:
result = self._upscale_video(
input_path, output_path, scale, model_name,
face_enhance, denoise_strength,
)
else:
result = self._upscale_image(
input_path, output_path, scale, model_name,
face_enhance, denoise_strength,
)
except Exception as e:
return ToolResult(success=False, error=f"Upscale failed: {e}")
elapsed = time.time() - start
return ToolResult(
success=True,
data={
"input": str(input_path),
"output": str(output_path),
"scale": scale,
"model": model_name,
"face_enhance": face_enhance,
"type": "video" if is_video else "image",
**result,
},
artifacts=[str(output_path)],
duration_seconds=round(elapsed, 2),
)
# ------------------------------------------------------------------
# Image upscaling
# ------------------------------------------------------------------
def _upscale_image(
self,
input_path: Path,
output_path: Path,
scale: int,
model_name: str,
face_enhance: bool,
denoise_strength: float,
) -> dict[str, Any]:
import cv2
upsampler = self._build_upsampler(scale, model_name, denoise_strength, face_enhance)
img = cv2.imread(str(input_path), cv2.IMREAD_UNCHANGED)
if img is None:
raise ValueError(f"Could not read image: {input_path}")
output, _ = upsampler.enhance(img, outscale=scale)
cv2.imwrite(str(output_path), output)
h, w = output.shape[:2]
return {"output_width": w, "output_height": h}
# ------------------------------------------------------------------
# Video upscaling
# ------------------------------------------------------------------
def _upscale_video(
self,
input_path: Path,
output_path: Path,
scale: int,
model_name: str,
face_enhance: bool,
denoise_strength: float,
) -> dict[str, Any]:
import cv2
upsampler = self._build_upsampler(scale, model_name, denoise_strength, face_enhance)
# Get source frame rate
fps = self._get_video_fps(input_path)
with tempfile.TemporaryDirectory() as tmpdir:
frames_dir = Path(tmpdir) / "frames"
upscaled_dir = Path(tmpdir) / "upscaled"
frames_dir.mkdir()
upscaled_dir.mkdir()
# Extract frames
self.run_command([
"ffmpeg", "-y",
"-i", str(input_path),
str(frames_dir / "frame_%06d.png"),
])
# Upscale each frame
frame_files = sorted(frames_dir.glob("*.png"))
total_frames = len(frame_files)
for frame_file in frame_files:
img = cv2.imread(str(frame_file), cv2.IMREAD_UNCHANGED)
output, _ = upsampler.enhance(img, outscale=scale)
cv2.imwrite(str(upscaled_dir / frame_file.name), output)
# Reassemble with ffmpeg, copy audio from original
reassemble_cmd = [
"ffmpeg", "-y",
"-framerate", str(fps),
"-i", str(upscaled_dir / "frame_%06d.png"),
"-i", str(input_path),
"-map", "0:v",
"-map", "1:a?",
"-c:v", "libx264", "-crf", "18",
"-c:a", "copy",
"-pix_fmt", "yuv420p",
str(output_path),
]
self.run_command(reassemble_cmd)
return {"total_frames": total_frames, "fps": fps}
# ------------------------------------------------------------------
# Helpers
# ------------------------------------------------------------------
def _build_upsampler(
self,
scale: int,
model_name: str,
denoise_strength: float,
face_enhance: bool,
):
"""Build and return a RealESRGANer instance."""
import torch
from basicsr.archs.rrdbnet_arch import RRDBNet
from realesrgan import RealESRGANer
# Select architecture based on model
if model_name == "RealESRGAN_x4plus_anime_6B":
model = RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=6, num_grow_ch=32, scale=4)
else:
model = RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=4)
# Resolve model path — realesrgan ships weights or downloads them
model_url = f"https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/{model_name}.pth"
if model_name == "RealESRGAN_x4plus_anime_6B":
model_url = f"https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.2.4/{model_name}.pth"
half = torch.cuda.is_available()
upsampler = RealESRGANer(
scale=4,
model_path=model_url,
model=model,
dni_weight=denoise_strength,
half=half,
)
if face_enhance:
from gfpgan import GFPGANer
face_enhancer = GFPGANer(
model_path="https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.3.pth",
upscale=scale,
arch="clean",
channel_multiplier=2,
bg_upsampler=upsampler,
)
# Monkey-patch so the caller can use the same interface
original_enhance = upsampler.enhance
def enhance_with_face(img, outscale=scale):
_, _, output = face_enhancer.enhance(
img, has_aligned=False, only_center_face=False, paste_back=True,
)
return output, None
upsampler.enhance = enhance_with_face
return upsampler
def _get_video_fps(self, video_path: Path) -> float:
"""Extract frame rate from video using ffprobe."""
import json
if not shutil.which("ffprobe"):
return 30.0 # safe default
try:
proc = self.run_command([
"ffprobe", "-v", "quiet",
"-print_format", "json",
"-show_streams",
str(video_path),
])
probe = json.loads(proc.stdout)
for stream in probe.get("streams", []):
if stream.get("codec_type") == "video":
r_frame_rate = stream.get("r_frame_rate", "30/1")
num, den = r_frame_rate.split("/")
return round(int(num) / int(den), 3)
except Exception:
pass
return 30.0