Spaces:
Running on Zero
Running on Zero
Return a fast preview plus the lossless PNG, and isolate per-request files
Browse files
app.py
CHANGED
|
@@ -11,6 +11,7 @@ decision here, not only a memory one.
|
|
| 11 |
"""
|
| 12 |
import os
|
| 13 |
import re
|
|
|
|
| 14 |
import time
|
| 15 |
|
| 16 |
import gradio as gr
|
|
@@ -168,13 +169,19 @@ def _check(image):
|
|
| 168 |
return image.convert("RGB")
|
| 169 |
|
| 170 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 171 |
def restore(image):
|
| 172 |
"""The built-in page: both halves for the slider, plus a lossless download."""
|
| 173 |
image = _check(image)
|
| 174 |
t0 = time.time()
|
| 175 |
result = _run(image)
|
| 176 |
t_inf = time.time() - t0
|
| 177 |
-
path = os.path.join(
|
| 178 |
result.save(path, "PNG", optimize=False) # lossless, nothing added after the model
|
| 179 |
n = _tile_count(*image.size)
|
| 180 |
return (image, result), path, "%d by %d px, %d tiles, %.1f s on the GPU (%.2f s per tile)" % (
|
|
@@ -182,12 +189,21 @@ def restore(image):
|
|
| 182 |
|
| 183 |
|
| 184 |
def restore_api(image):
|
| 185 |
-
"""Lean endpoint for a standalone front end
|
| 186 |
|
| 187 |
-
|
| 188 |
-
|
|
|
|
|
|
|
|
|
|
| 189 |
"""
|
| 190 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 191 |
|
| 192 |
|
| 193 |
CSS = ".contain{max-width:1100px!important}"
|
|
@@ -220,9 +236,11 @@ with gr.Blocks(title="iPhoneBlur Restore") as demo:
|
|
| 220 |
# the caller can fetch; gr.api returned the repr of the image object instead.
|
| 221 |
with gr.Row(visible=False):
|
| 222 |
api_in = gr.Image(type="pil", image_mode="RGB", format="png")
|
| 223 |
-
|
|
|
|
| 224 |
api_btn = gr.Button("api")
|
| 225 |
-
api_btn.click(restore_api, inputs=api_in, outputs=
|
|
|
|
| 226 |
|
| 227 |
if __name__ == "__main__":
|
| 228 |
demo.queue(max_size=20).launch(css=CSS, theme=gr.themes.Soft(), show_error=True)
|
|
|
|
| 11 |
"""
|
| 12 |
import os
|
| 13 |
import re
|
| 14 |
+
import tempfile
|
| 15 |
import time
|
| 16 |
|
| 17 |
import gradio as gr
|
|
|
|
| 169 |
return image.convert("RGB")
|
| 170 |
|
| 171 |
|
| 172 |
+
def _outdir():
|
| 173 |
+
"""A fresh directory per request. A fixed filename would let two visitors
|
| 174 |
+
overwrite each other's result."""
|
| 175 |
+
return tempfile.mkdtemp(prefix="restore_")
|
| 176 |
+
|
| 177 |
+
|
| 178 |
def restore(image):
|
| 179 |
"""The built-in page: both halves for the slider, plus a lossless download."""
|
| 180 |
image = _check(image)
|
| 181 |
t0 = time.time()
|
| 182 |
result = _run(image)
|
| 183 |
t_inf = time.time() - t0
|
| 184 |
+
path = os.path.join(_outdir(), "restored.png")
|
| 185 |
result.save(path, "PNG", optimize=False) # lossless, nothing added after the model
|
| 186 |
n = _tile_count(*image.size)
|
| 187 |
return (image, result), path, "%d by %d px, %d tiles, %.1f s on the GPU (%.2f s per tile)" % (
|
|
|
|
| 189 |
|
| 190 |
|
| 191 |
def restore_api(image):
|
| 192 |
+
"""Lean endpoint for a standalone front end.
|
| 193 |
|
| 194 |
+
Returns two files rather than one: a small preview to show straight away, and the
|
| 195 |
+
lossless PNG. Both come back as URLs, so the browser only pulls the big one if the
|
| 196 |
+
visitor actually clicks download. Measured on a 0.9 Mbps line, the PNG alone took
|
| 197 |
+
23.6 s to transfer while the GPU work took 4.2 s, so the preview is what makes this
|
| 198 |
+
usable on a slow connection. Nothing is re-compressed in the file people keep.
|
| 199 |
"""
|
| 200 |
+
result = _run(_check(image))
|
| 201 |
+
d = _outdir()
|
| 202 |
+
png = os.path.join(d, "restored.png")
|
| 203 |
+
preview = os.path.join(d, "preview.webp")
|
| 204 |
+
result.save(png, "PNG", optimize=False)
|
| 205 |
+
result.save(preview, "WEBP", quality=92, method=4)
|
| 206 |
+
return preview, png
|
| 207 |
|
| 208 |
|
| 209 |
CSS = ".contain{max-width:1100px!important}"
|
|
|
|
| 236 |
# the caller can fetch; gr.api returned the repr of the image object instead.
|
| 237 |
with gr.Row(visible=False):
|
| 238 |
api_in = gr.Image(type="pil", image_mode="RGB", format="png")
|
| 239 |
+
api_preview = gr.File() # gr.File passes bytes through untouched,
|
| 240 |
+
api_full = gr.File() # gr.Image would re-encode and break "lossless"
|
| 241 |
api_btn = gr.Button("api")
|
| 242 |
+
api_btn.click(restore_api, inputs=api_in, outputs=[api_preview, api_full],
|
| 243 |
+
api_name="restore_image")
|
| 244 |
|
| 245 |
if __name__ == "__main__":
|
| 246 |
demo.queue(max_size=20).launch(css=CSS, theme=gr.themes.Soft(), show_error=True)
|