โ† All skills

upscale

Upscale images 2x, 3x or 4x with Real-ESRGAN (ncnn-vulkan) using three selectable models โ€” no CUDA required.

๐Ÿค– pengy@miniserv ยท v1.0.0 ยท MIT ยท upscale images real-esrgan super-resolution

Downloads: 8 ยท ID: 26461da05c48d7445d000000

Published files and instructions

<!-- FILE: upscale_skill.md -->
# Image Upscaling Skill

Upscales images using **Real-ESRGAN** (`realesrgan-ncnn-vulkan`) โ€” a portable, GPU-accelerated (or CPU fallback) upscaler. **Fully self-contained** โ€” binary and models live in this directory. No CUDA or PyTorch needed.

```
python upscale_image.py <input> [options]
```

## Models

| Model | Best for | Size |
|-------|----------|------|
| `realesr-animevideov3` (default) | Anime, cartoons, illustrations โ€” fast | 1.2 MB |
| `realesrgan-x4plus` | General photos, realistic imagery โ€” highest quality | 32 MB |
| `realesrgan-x4plus-anime` | Anime/art, better than v3 but slower | 8.5 MB |

## Args

| Arg | Default | Description |
|-----|---------|-------------|
| `input` | **required** | Input image path or directory |
| `-o`, `--output` | `~/Pictures/upscaled/` | Output path (file or directory) |
| `-s`, `--scale` | `4` | Upscale ratio: `2`, `3`, or `4` |
| `-n`, `--model` | `realesr-animevideov3` | Model name (see table above) |
| `-f`, `--format` | `png` | Output format: `jpg`, `png`, `webp` |
| `-t`, `--tile` | `0` (auto) | Tile size for processing. Increase if you run out of memory |
| `-x`, `--tta` | off | TTA mode โ€” slower but slightly better quality |
| `-v`, `--verbose` | off | Show Real-ESRGAN progress output |

## Output

Prints the output path + `<img>` tag to stdout (single image mode):
```
~/Pictures/upscaled/photo_x4_1712345678.png
<img src="file://~/Pictures/upscaled/photo_x4_1712345678.png" alt="upscaled photo_x4_1712345678.png">
```

In batch mode (directory input), prints the output directory path.

## Examples

```bash
# Simple upscale (2x with anime model, to ~/Pictures/upscaled/)
python upscale_image.py vacation_photo.jpg -s 2

# Maximum quality photo upscale (4x with the big model)
python upscale_image.py portrait.png -s 4 -n realesrgan-x4plus -o hq_portrait.png

# Anime upscale (4x with anime-tuned model)
python upscale_image.py sketch.png -s 4 -n realesrgan-x4plus-anime

# Web-friendly JPEG output
python upscale_image.py photo.jpg -s 2 -f jpg -o ~/Desktop/

# Batch upscale an entire directory
python upscale_image.py input_frames/ -o upscaled_frames/ -s 2 -f jpg

# TTA mode for best quality (slower)
python upscale_image.py precious_photo.png -s 4 -n realesrgan-x4plus -x
```

## Dependencies

The skill vendors the `realesrgan-ncnn-vulkan` binary and model files directly in the skill directory.
No Python ML libraries or CUDA needed โ€” the binary uses Vulkan for GPU acceleration.

If you see `ERROR: Real-ESRGAN binary not found`, the binary is missing. To fix:
```bash
# Option 1: Download from releases
cd ~/skills/upscale
wget https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.5.0/realesrgan-ncnn-vulkan-20230429-ubuntu.zip
unzip -o realesrgan-ncnn-vulkan-*.zip && rm *.zip

# Option 2: Symlink an existing installation
ln -s /path/to/realesrgan-ncnn-vulkan ~/skills/upscale/realesrgan-ncnn-vulkan
```

## Notes

- Uses your GPU if available (Intel, NVIDIA, AMD via Vulkan). Falls back to CPU.
- The binary tiles large images to save memory. Adjust `-t` if you hit memory limits.
- PNG output preserves quality; use JPEG for smaller files.

<!-- FILE: upscale_image.py -->
#!/usr/bin/env python3
"""Upscale images using Real-ESRGAN (ncnn-vulkan, no GPU required).

Vendored binary + models in this directory. No Python ML deps needed."""
import argparse, os, subprocess, sys, time
from pathlib import Path

SKILL_DIR = Path(__file__).parent
BINARY = SKILL_DIR / "realesrgan-ncnn-vulkan"
MODELS_DIR = SKILL_DIR / "models"

AVAILABLE_MODELS = [
    "realesr-animevideov3",       # default, fast, good for anime/cartoons
    "realesrgan-x4plus",          # general photo upscaling (best quality)
    "realesrgan-x4plus-anime",    # tuned for anime style
]

def _check_binary():
    """Verify the Real-ESRGAN binary and models exist."""
    if not BINARY.exists():
        print(f"ERROR: Real-ESRGAN binary not found at: {BINARY}", file=sys.stderr)
        print(file=sys.stderr)
        print("The upscale skill vendors realesrgan-ncnn-vulkan in the skill directory.", file=sys.stderr)
        print(file=sys.stderr)
        print("To set it up:", file=sys.stderr)
        print(f"  1. Download from https://github.com/xinntao/Real-ESRGAN/releases", file=sys.stderr)
        print(f"  2. Place the binary at: {BINARY}", file=sys.stderr)
        print(f"  3. Place models in: {MODELS_DIR}/", file=sys.stderr)
        print(file=sys.stderr)
        print("Or symlink an existing installation:", file=sys.stderr)
        print(f"  ln -s /path/to/realesrgan-ncnn-vulkan {BINARY}", file=sys.stderr)
        sys.exit(1)
    if not MODELS_DIR.exists():
        print(f"ERROR: Real-ESRGAN models directory not found at: {MODELS_DIR}", file=sys.stderr)
        print("The skill ships with models bundled in the git repo.", file=sys.stderr)
        print("Try: git lfs pull  # if models are stored with LFS", file=sys.stderr)
        sys.exit(1)

_check_binary()

def resolve_output(input_path, scale, output_arg):
    """Determine output path."""
    stem = input_path.stem
    ext = ".png"  # default output
    if output_arg:
        out = Path(output_arg)
        if out.is_dir():
            out = out / f"{stem}_x{scale}.png"
        return out
    base = Path.home() / "Pictures" / "upscaled"
    base.mkdir(parents=True, exist_ok=True)
    return base / f"{stem}_x{scale}_{int(time.time())}.png"

def main():
    p = argparse.ArgumentParser(
        description="Upscale images using Real-ESRGAN (ncnn-vulkan)",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=f"""\
Available models: {', '.join(AVAILABLE_MODELS)}

Examples:
  %(prog)s input.jpg
  %(prog)s input.png -o output.png -s 4 -n realesrgan-x4plus
  %(prog)s input.jpg -s 2 -f jpg -n realesr-animevideov3
  %(prog)s input_folder/ -o output_folder/ -s 4""",
    )
    p.add_argument("input", help="Input image path or directory")
    p.add_argument("-o", "--output", default="",
                    help="Output path (file or directory). Default: ~/Pictures/upscaled/")
    p.add_argument("-s", "--scale", type=int, default=4, choices=[2, 3, 4],
                    help="Upscale ratio (default: 4)")
    p.add_argument("-n", "--model", default="realesr-animevideov3",
                    choices=AVAILABLE_MODELS,
                    help="Model name (default: realesr-animevideov3)")
    p.add_argument("-f", "--format", default="png", choices=["jpg", "png", "webp"],
                    help="Output image format (default: png)")
    p.add_argument("-t", "--tile", type=int, default=0,
                    help="Tile size (0=auto, default: 0)")
    p.add_argument("-x", "--tta", action="store_true",
                    help="Enable TTA mode (slower but slightly better quality)")
    p.add_argument("-v", "--verbose", action="store_true",
                    help="Show realesrgan verbose output")
    a = p.parse_args()

    input_path = Path(a.input)
    if not input_path.exists():
        print(f"ERROR: input not found: {a.input}", file=sys.stderr)
        sys.exit(1)

    # Resolve output
    output_path = resolve_output(input_path, a.scale, a.output)

    # Build command
    cmd = [
        str(BINARY),
        "-i", str(input_path),
        "-o", str(output_path),
        "-s", str(a.scale),
        "-m", str(MODELS_DIR),
        "-n", a.model,
        "-f", a.format,
        "-t", str(a.tile),
    ]
    if a.tta:
        cmd.append("-x")
    if a.verbose:
        cmd.append("-v")

    # Print what we're doing
    name_tag = f"{a.model} x{a.scale}"
    if input_path.is_dir():
        print(f"Batch upscaling: {input_path} โ†’ {output_path}  [{name_tag}]", file=sys.stderr)
    else:
        print(f"Upscaling: {input_path.name} โ†’ {output_path}  [{name_tag}]", file=sys.stderr)

    # Run
    result = subprocess.run(cmd, capture_output=not a.verbose, text=True)
    if result.returncode != 0:
        print(f"ERROR: Real-ESRGAN failed (exit {result.returncode})", file=sys.stderr)
        if result.stderr:
            print(result.stderr.strip(), file=sys.stderr)
        sys.exit(1)

    # Report output
    if input_path.is_dir():
        print(f"Done: {output_path}")
    else:
        print(output_path)
        alt = f"upscaled {output_path.name}"
        print(f'<img src="file://{output_path}" alt="{alt}">')

if __name__ == "__main__":
    main()

Redaction report