minhajulofficial commited on
Commit
9d97005
·
verified ·
1 Parent(s): 276f8cd

Initial PixelBoost AI upscaler (Real-ESRGAN)

Browse files
Files changed (4) hide show
  1. .gitignore +5 -0
  2. README.md +28 -6
  3. app.py +133 -0
  4. requirements.txt +8 -0
.gitignore ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ weights/
2
+ __pycache__/
3
+ *.pyc
4
+ .gradio/
5
+ flagged/
README.md CHANGED
@@ -1,13 +1,35 @@
1
  ---
2
- title: Pixelboost Upscaler
3
- emoji: 👀
4
- colorFrom: red
5
  colorTo: pink
6
  sdk: gradio
7
- sdk_version: 6.14.0
8
- python_version: '3.13'
9
  app_file: app.py
10
  pinned: false
 
 
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: PixelBoost AI Upscaler
3
+ emoji:
4
+ colorFrom: purple
5
  colorTo: pink
6
  sdk: gradio
7
+ sdk_version: 4.44.1
 
8
  app_file: app.py
9
  pinned: false
10
+ license: mit
11
+ short_description: Real-ESRGAN AI image upscaler for PixelBoost
12
  ---
13
 
14
+ # PixelBoost AI Upscaler
15
+
16
+ Real-ESRGAN (`realesr-general-x4v3`) behind a Gradio interface. This Space
17
+ powers the **AI Enhance** mode of [PixelBoost](https://pixelboost-upscaler.pages.dev/).
18
+ The Space exposes an `/upscale` API endpoint that the PixelBoost FastAPI
19
+ backend calls via `gradio_client`.
20
+
21
+ ## Local dev
22
+
23
+ ```bash
24
+ pip install -r requirements.txt
25
+ python app.py
26
+ ```
27
+
28
+ ## Notes
29
+
30
+ - Free CPU tier: expect 20-90s per image. Tiling (256px) keeps memory low.
31
+ - Model weights download from the official Real-ESRGAN GitHub release on
32
+ first boot and are cached in `./weights`.
33
+ - Source-of-truth lives in
34
+ [pixelboost-upscaler/hf-space](https://github.com/minhajulofficial/pixelboost-upscaler/tree/main/hf-space);
35
+ push changes from there.
app.py ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """PixelBoost AI upscaler — HuggingFace Space (Gradio).
2
+
3
+ Real-ESRGAN inference behind a Gradio interface. The PixelBoost backend
4
+ (FastAPI) calls this Space's ``/upscale`` API endpoint when the user picks
5
+ "AI Enhance" mode. The Gradio UI is also usable directly for manual testing.
6
+
7
+ Model: realesr-general-x4v3 (5MB, SRVGG-based) — chosen for fast CPU
8
+ inference on HF Spaces' free tier. Quality is excellent on photos with the
9
+ mild denoise strength applied here.
10
+
11
+ Scales:
12
+ - 4x: native single forward pass through the 4x model.
13
+ - 2x: 4x forward pass + LANCZOS downscale to 2x (cheaper than a separate 2x
14
+ model and produces sharper results than a pure 2x model on free CPU).
15
+ - 6x: 4x forward pass + LANCZOS upscale to 6x.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import os
21
+ import sys
22
+ import types
23
+
24
+ import gradio as gr
25
+ import numpy as np
26
+ import torch
27
+ from PIL import Image
28
+
29
+ # --- torchvision compat shim ---------------------------------------------------
30
+ # basicsr (transitively via realesrgan) imports
31
+ # ``torchvision.transforms.functional_tensor`` which was removed in
32
+ # torchvision >= 0.17. Recreate the module pointing at ``functional`` before
33
+ # basicsr is imported.
34
+ try:
35
+ import torchvision.transforms.functional_tensor # type: ignore[import-not-found] # noqa: F401
36
+ except ImportError:
37
+ from torchvision.transforms import functional as _tv_functional
38
+
39
+ _shim = types.ModuleType("torchvision.transforms.functional_tensor")
40
+ _shim.rgb_to_grayscale = _tv_functional.rgb_to_grayscale # type: ignore[attr-defined]
41
+ sys.modules["torchvision.transforms.functional_tensor"] = _shim
42
+
43
+ # --- model setup --------------------------------------------------------------
44
+ from basicsr.archs.srvgg_arch import SRVGGNetCompact # noqa: E402
45
+ from basicsr.utils.download_util import load_file_from_url # noqa: E402
46
+ from realesrgan import RealESRGANer # noqa: E402
47
+
48
+ MODEL_NAME = "realesr-general-x4v3"
49
+ MODEL_URL = (
50
+ "https://github.com/xinntao/Real-ESRGAN/releases/download/"
51
+ "v0.2.5.0/realesr-general-x4v3.pth"
52
+ )
53
+ WEIGHTS_DIR = os.environ.get("PIXELBOOST_WEIGHTS_DIR", "weights")
54
+ TILE_SIZE = int(os.environ.get("PIXELBOOST_TILE_SIZE", "256"))
55
+ MAX_INPUT_PIXELS = int(os.environ.get("PIXELBOOST_MAX_INPUT_PIXELS", str(4_000_000)))
56
+
57
+
58
+ def _build_upscaler() -> RealESRGANer:
59
+ os.makedirs(WEIGHTS_DIR, exist_ok=True)
60
+ model_path = load_file_from_url(
61
+ url=MODEL_URL, model_dir=WEIGHTS_DIR, file_name=f"{MODEL_NAME}.pth"
62
+ )
63
+ arch = SRVGGNetCompact(
64
+ num_in_ch=3,
65
+ num_out_ch=3,
66
+ num_feat=64,
67
+ num_conv=32,
68
+ upscale=4,
69
+ act_type="prelu",
70
+ )
71
+ return RealESRGANer(
72
+ scale=4,
73
+ model_path=model_path,
74
+ model=arch,
75
+ tile=TILE_SIZE,
76
+ tile_pad=10,
77
+ pre_pad=0,
78
+ half=False, # CPU does not support fp16
79
+ device=torch.device("cpu"),
80
+ )
81
+
82
+
83
+ print(f"[pixelboost] loading {MODEL_NAME}...", flush=True)
84
+ UPSAMPLER = _build_upscaler()
85
+ print(f"[pixelboost] model loaded; tile={TILE_SIZE}", flush=True)
86
+
87
+
88
+ # --- inference ----------------------------------------------------------------
89
+ def upscale(image: Image.Image | None, scale: int = 4) -> Image.Image:
90
+ """Run Real-ESRGAN inference and resize to the requested scale."""
91
+ if image is None:
92
+ raise gr.Error("No image provided.")
93
+ if int(scale) not in {2, 4, 6}:
94
+ raise gr.Error("Scale must be 2, 4, or 6.")
95
+
96
+ image = image.convert("RGB")
97
+ if image.width * image.height > MAX_INPUT_PIXELS:
98
+ raise gr.Error(
99
+ f"Input too large ({image.width}x{image.height}). "
100
+ f"Max ~{MAX_INPUT_PIXELS // 1_000_000} megapixels in AI mode."
101
+ )
102
+
103
+ arr = np.array(image)
104
+ output, _ = UPSAMPLER.enhance(arr, outscale=4)
105
+ result = Image.fromarray(output)
106
+
107
+ target = (image.width * int(scale), image.height * int(scale))
108
+ if result.size != target:
109
+ result = result.resize(target, Image.Resampling.LANCZOS)
110
+ return result
111
+
112
+
113
+ demo = gr.Interface(
114
+ fn=upscale,
115
+ inputs=[
116
+ gr.Image(type="pil", label="Input image"),
117
+ gr.Radio([2, 4, 6], value=4, label="Scale", type="value"),
118
+ ],
119
+ outputs=gr.Image(type="pil", label="Upscaled", format="png"),
120
+ title="PixelBoost AI Upscaler",
121
+ description=(
122
+ "Real-ESRGAN (realesr-general-x4v3) running on HuggingFace free CPU. "
123
+ "Expect 20-90s per image. Called from the PixelBoost backend when "
124
+ "users pick AI Enhance mode."
125
+ ),
126
+ api_name="upscale",
127
+ allow_flagging="never",
128
+ concurrency_limit=1,
129
+ )
130
+ demo.queue(max_size=20)
131
+
132
+ if __name__ == "__main__":
133
+ demo.launch(show_api=True)
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ torch==2.2.2
2
+ torchvision==0.17.2
3
+ numpy<2.0
4
+ pillow>=10.0.0
5
+ opencv-python-headless>=4.8.0
6
+ basicsr==1.4.2
7
+ realesrgan==0.3.0
8
+ gradio>=4.44.0