throwaway74 commited on
Commit
6bbc64b
·
verified ·
1 Parent(s): 29af832

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +168 -230
app.py CHANGED
@@ -1,8 +1,9 @@
1
  import os
2
  import uuid
3
- import html
4
  import gradio as gr
5
  import spaces
 
6
  from PIL import Image
7
  from image_gen_aux import UpscaleWithModel
8
 
@@ -11,10 +12,8 @@ from image_gen_aux import UpscaleWithModel
11
  # ---------------------------------
12
 
13
  BASE_TMP_DIR = "/tmp/image_enhancer"
14
- ORIGINAL_DIR = os.path.join(BASE_TMP_DIR, "original")
15
  ENHANCED_DIR = os.path.join(BASE_TMP_DIR, "enhanced")
16
 
17
- os.makedirs(ORIGINAL_DIR, exist_ok=True)
18
  os.makedirs(ENHANCED_DIR, exist_ok=True)
19
 
20
  # ---------------------------------
@@ -38,145 +37,6 @@ RATIO_MAP = {
38
  "3:2": (3, 2),
39
  }
40
 
41
- # ---------------------------------
42
- # Frontend JS/CSS
43
- # ---------------------------------
44
-
45
- HEAD = """
46
- <style>
47
- .preview-shell {
48
- background: #1f1f1f;
49
- border: 1px solid #3a3a3a;
50
- border-radius: 10px;
51
- padding: 14px;
52
- }
53
- .preview-toolbar {
54
- display: flex;
55
- flex-wrap: wrap;
56
- gap: 10px;
57
- align-items: center;
58
- margin-bottom: 12px;
59
- }
60
- .preview-btn {
61
- background: #2a2a2a;
62
- color: white;
63
- border: 1px solid #4a4a4a;
64
- border-radius: 8px;
65
- padding: 8px 12px;
66
- cursor: pointer;
67
- font-size: 14px;
68
- }
69
- .preview-btn:hover {
70
- background: #343434;
71
- }
72
- .preview-check {
73
- display: inline-flex;
74
- align-items: center;
75
- gap: 8px;
76
- color: white;
77
- font-size: 14px;
78
- }
79
- .preview-slider-wrap {
80
- display: inline-flex;
81
- align-items: center;
82
- gap: 8px;
83
- color: white;
84
- font-size: 14px;
85
- }
86
- .preview-stage {
87
- position: relative;
88
- width: 100%;
89
- min-height: 320px;
90
- background: #181818;
91
- border: 1px solid #333;
92
- border-radius: 10px;
93
- overflow: hidden;
94
- display: flex;
95
- align-items: center;
96
- justify-content: center;
97
- }
98
- .preview-base {
99
- display: block;
100
- width: 100%;
101
- height: auto;
102
- max-height: 70vh;
103
- object-fit: contain;
104
- }
105
- .preview-overlay {
106
- position: absolute;
107
- inset: 0;
108
- width: 100%;
109
- height: 100%;
110
- object-fit: contain;
111
- pointer-events: none;
112
- }
113
- </style>
114
-
115
- <script>
116
- function setPreviewMode(rootId, mode) {
117
- const root = document.getElementById(rootId);
118
- if (!root) return;
119
-
120
- const base = root.querySelector(".preview-base");
121
- const overlay = root.querySelector(".preview-overlay");
122
- const blendToggle = root.querySelector(".blend-toggle");
123
-
124
- const enhancedSrc = root.getAttribute("data-enhanced-src");
125
- const originalSrc = root.getAttribute("data-original-src");
126
-
127
- if (!base || !overlay || !blendToggle) return;
128
-
129
- if (mode === "enhanced") {
130
- base.src = enhancedSrc;
131
- overlay.style.display = blendToggle.checked ? "block" : "none";
132
- overlay.src = originalSrc;
133
- } else if (mode === "original") {
134
- base.src = originalSrc;
135
- overlay.style.display = "none";
136
- }
137
- }
138
-
139
- function toggleBlend(rootId) {
140
- const root = document.getElementById(rootId);
141
- if (!root) return;
142
-
143
- const overlay = root.querySelector(".preview-overlay");
144
- const blendToggle = root.querySelector(".blend-toggle");
145
- const opacityWrap = root.querySelector(".opacity-wrap");
146
- const base = root.querySelector(".preview-base");
147
-
148
- const originalSrc = root.getAttribute("data-original-src");
149
- const enhancedSrc = root.getAttribute("data-enhanced-src");
150
-
151
- if (!overlay || !blendToggle || !opacityWrap || !base) return;
152
-
153
- if (blendToggle.checked) {
154
- base.src = enhancedSrc;
155
- overlay.src = originalSrc;
156
- overlay.style.display = "block";
157
- opacityWrap.style.display = "inline-flex";
158
- } else {
159
- overlay.style.display = "none";
160
- opacityWrap.style.display = "none";
161
- }
162
- }
163
-
164
- function updateBlendOpacity(rootId, value) {
165
- const root = document.getElementById(rootId);
166
- if (!root) return;
167
-
168
- const overlay = root.querySelector(".preview-overlay");
169
- const valueLabel = root.querySelector(".opacity-value");
170
-
171
- if (!overlay || !valueLabel) return;
172
-
173
- const alpha = Math.max(0, Math.min(100, Number(value))) / 100.0;
174
- overlay.style.opacity = alpha.toString();
175
- valueLabel.textContent = value + "%";
176
- }
177
- </script>
178
- """
179
-
180
  # ---------------------------------
181
  # Helpers
182
  # ---------------------------------
@@ -210,82 +70,105 @@ def get_tile_dimensions(ratio_name: str, tile_preset: str):
210
 
211
  def update_tile_display(ratio_name: str, tile_preset: str):
212
  tile_width, tile_height = get_tile_dimensions(ratio_name, tile_preset)
213
- return f"**Tile Width:** {tile_width}px \n**Tile Height:** {tile_height}px"
 
 
 
214
 
215
 
216
- def save_input_image(input_image_np):
217
- img = Image.fromarray(input_image_np).convert("RGB")
218
- path = os.path.join(ORIGINAL_DIR, f"{uuid.uuid4().hex}.png")
219
- img.save(path, format="PNG", compress_level=0)
220
- return img, path
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
 
222
 
223
- def save_output_image(output_img):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
  output_img = output_img.convert("RGB")
225
- path = os.path.join(ENHANCED_DIR, f"{uuid.uuid4().hex}.png")
226
- output_img.save(path, format="PNG", compress_level=0)
227
- return path
228
 
 
 
 
 
 
 
229
 
230
- def to_file_url(path: str):
231
- return f"/gradio_api/file={path}"
232
-
233
-
234
- def build_preview_html(original_path: str, enhanced_path: str):
235
- root_id = f"preview_{uuid.uuid4().hex}"
236
- original_url = html.escape(to_file_url(original_path), quote=True)
237
- enhanced_url = html.escape(to_file_url(enhanced_path), quote=True)
238
-
239
- return f"""
240
- <div
241
- id="{root_id}"
242
- class="preview-shell"
243
- data-original-src="{original_url}"
244
- data-enhanced-src="{enhanced_url}"
245
- >
246
- <div class="preview-toolbar">
247
- <button class="preview-btn" onclick="setPreviewMode('{root_id}', 'enhanced')">
248
- Show Enhanced
249
- </button>
250
-
251
- <button class="preview-btn" onclick="setPreviewMode('{root_id}', 'original')">
252
- Show Original
253
- </button>
254
-
255
- <label class="preview-check">
256
- <input
257
- type="checkbox"
258
- class="blend-toggle"
259
- onchange="toggleBlend('{root_id}')"
260
- >
261
- Blend Overlay Mode
262
- </label>
263
-
264
- <div class="preview-slider-wrap opacity-wrap" style="display:none;">
265
- <span>Overlay Opacity</span>
266
- <input
267
- type="range"
268
- min="0"
269
- max="100"
270
- value="50"
271
- class="opacity-slider"
272
- oninput="updateBlendOpacity('{root_id}', this.value)"
273
- >
274
- <span class="opacity-value">50%</span>
275
- </div>
276
- </div>
277
-
278
- <div class="preview-stage">
279
- <img class="preview-base" src="{enhanced_url}" alt="Enhanced Preview">
280
- <img
281
- class="preview-overlay"
282
- src="{original_url}"
283
- alt="Original Overlay"
284
- style="display:none; opacity:0.5;"
285
- >
286
- </div>
287
- </div>
288
- """
289
 
290
 
291
  # ---------------------------------
@@ -293,38 +176,61 @@ def build_preview_html(original_path: str, enhanced_path: str):
293
  # ---------------------------------
294
 
295
  @spaces.GPU
296
- def upscale(enhancer_type, ratio_name, tile_preset, input_image):
 
 
 
 
 
 
 
297
  if input_image is None:
298
- return "<div class='preview-shell'>No preview available yet.</div>", None
 
 
 
 
299
 
300
- original_img, original_path = save_input_image(input_image)
 
301
 
302
  tile_width, tile_height = get_tile_dimensions(ratio_name, tile_preset)
303
  upscaler = get_upscaler(enhancer_type)
304
 
305
- enhanced = upscaler(
306
- original_img,
307
- tiling=True,
308
  tile_width=tile_width,
309
  tile_height=tile_height,
 
310
  )
311
 
312
- if not isinstance(enhanced, Image.Image):
313
- enhanced = Image.fromarray(enhanced)
 
 
314
 
315
- enhanced_path = save_output_image(enhanced)
316
- preview_html = build_preview_html(original_path, enhanced_path)
 
 
 
 
 
 
 
317
 
318
- return preview_html, enhanced_path
319
 
320
 
321
  # ---------------------------------
322
  # UI
323
  # ---------------------------------
324
 
325
- with gr.Blocks(head=HEAD) as demo:
326
  gr.Markdown("# Image Enhancer")
327
 
 
328
  with gr.Group():
329
  enhancer_type = gr.Radio(
330
  choices=["Anime Enhancer", "Photo Enhancer"],
@@ -332,12 +238,13 @@ with gr.Blocks(head=HEAD) as demo:
332
  label="Enhancer Type"
333
  )
334
 
 
335
  with gr.Group():
336
  gr.Markdown("### Tile Settings")
337
 
338
  ratio_name = gr.Radio(
339
  choices=["16:9", "9:16", "4:5", "1:1", "5:4", "2:3", "3:2"],
340
- value="16:9",
341
  label="Aspect Ratio"
342
  )
343
 
@@ -348,9 +255,26 @@ with gr.Blocks(head=HEAD) as demo:
348
  )
349
 
350
  tile_display = gr.Markdown(
351
- value=update_tile_display("16:9", "768")
352
  )
353
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
354
  with gr.Group():
355
  input_image = gr.Image(
356
  type="numpy",
@@ -358,20 +282,31 @@ with gr.Blocks(head=HEAD) as demo:
358
  height=400
359
  )
360
 
361
- run_button = gr.Button("Enhance 4x")
362
 
 
363
  with gr.Group():
364
  gr.Markdown("### Output Preview")
365
- preview_html = gr.HTML(
366
- value="<div class='preview-shell'>No preview available yet.</div>"
 
 
367
  )
368
 
 
369
  with gr.Group():
370
  gr.Markdown("### New Enhanced Image File")
371
  download_file = gr.File(
372
  label="Download new enhanced image file"
373
  )
374
 
 
 
 
 
 
 
 
375
  ratio_name.change(
376
  fn=update_tile_display,
377
  inputs=[ratio_name, tile_preset],
@@ -385,16 +320,19 @@ with gr.Blocks(head=HEAD) as demo:
385
  )
386
 
387
  run_button.click(
388
- fn=upscale,
389
  inputs=[
390
  enhancer_type,
391
  ratio_name,
392
  tile_preset,
 
 
393
  input_image,
394
  ],
395
  outputs=[
396
- preview_html,
397
  download_file,
 
398
  ],
399
  show_progress=True
400
  )
 
1
  import os
2
  import uuid
3
+ import math
4
  import gradio as gr
5
  import spaces
6
+ import numpy as np
7
  from PIL import Image
8
  from image_gen_aux import UpscaleWithModel
9
 
 
12
  # ---------------------------------
13
 
14
  BASE_TMP_DIR = "/tmp/image_enhancer"
 
15
  ENHANCED_DIR = os.path.join(BASE_TMP_DIR, "enhanced")
16
 
 
17
  os.makedirs(ENHANCED_DIR, exist_ok=True)
18
 
19
  # ---------------------------------
 
37
  "3:2": (3, 2),
38
  }
39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  # ---------------------------------
41
  # Helpers
42
  # ---------------------------------
 
70
 
71
  def update_tile_display(ratio_name: str, tile_preset: str):
72
  tile_width, tile_height = get_tile_dimensions(ratio_name, tile_preset)
73
+ return (
74
+ f"**Tile Width:** {tile_width}px \n"
75
+ f"**Tile Height:** {tile_height}px"
76
+ )
77
 
78
 
79
+ def format_megapixels(width: int, height: int) -> str:
80
+ mp = (width * height) / 1_000_000
81
+ return f"{mp:.2f} MP"
82
+
83
+
84
+ def format_file_size(num_bytes: int) -> str:
85
+ if num_bytes < 1024:
86
+ return f"{num_bytes} B"
87
+ if num_bytes < 1024 ** 2:
88
+ return f"{num_bytes / 1024:.1f} KB"
89
+ if num_bytes < 1024 ** 3:
90
+ return f"{num_bytes / (1024 ** 2):.2f} MB"
91
+ return f"{num_bytes / (1024 ** 3):.2f} GB"
92
+
93
+
94
+ def build_stats_markdown(
95
+ original_width: int,
96
+ original_height: int,
97
+ enhanced_width: int,
98
+ enhanced_height: int,
99
+ file_size_bytes: int,
100
+ export_format: str,
101
+ scale_mode: str,
102
+ ):
103
+ return (
104
+ f"**Original Dimensions:** {original_width} × {original_height}px \n"
105
+ f"**Original Megapixels:** {format_megapixels(original_width, original_height)} \n\n"
106
+ f"**Enhanced Dimensions:** {enhanced_width} × {enhanced_height}px \n"
107
+ f"**Enhanced Megapixels:** {format_megapixels(enhanced_width, enhanced_height)} \n\n"
108
+ f"**Scale Mode:** {scale_mode} \n"
109
+ f"**Export Format:** {export_format} \n"
110
+ f"**Saved File Size:** {format_file_size(file_size_bytes)}"
111
+ )
112
 
113
 
114
+ def upscale_once_with_model(
115
+ img: Image.Image,
116
+ upscaler,
117
+ tile_width: int,
118
+ tile_height: int,
119
+ ) -> Image.Image:
120
+ out = upscaler(
121
+ img,
122
+ tiling=True,
123
+ tile_width=tile_width,
124
+ tile_height=tile_height,
125
+ )
126
+
127
+ if not isinstance(out, Image.Image):
128
+ out = Image.fromarray(out)
129
+
130
+ return out.convert("RGB")
131
+
132
+
133
+ def run_scale_pipeline(
134
+ img: Image.Image,
135
+ upscaler,
136
+ tile_width: int,
137
+ tile_height: int,
138
+ scale_mode: str,
139
+ ) -> Image.Image:
140
+ # 4x = one model pass
141
+ if scale_mode == "4x":
142
+ return upscale_once_with_model(img, upscaler, tile_width, tile_height)
143
+
144
+ # 8x Experimental = one real 4x pass, then high-quality resize to 8x total
145
+ if scale_mode == "8x Experimental":
146
+ first = upscale_once_with_model(img, upscaler, tile_width, tile_height)
147
+ target_w = img.width * 8
148
+ target_h = img.height * 8
149
+ return first.resize((target_w, target_h), Image.LANCZOS).convert("RGB")
150
+
151
+ # 16x Experimental = two real 4x passes = 16x total
152
+ if scale_mode == "16x Experimental":
153
+ first = upscale_once_with_model(img, upscaler, tile_width, tile_height)
154
+ second = upscale_once_with_model(first, upscaler, tile_width, tile_height)
155
+ return second.convert("RGB")
156
+
157
+ return upscale_once_with_model(img, upscaler, tile_width, tile_height)
158
+
159
+
160
+ def save_output_image(output_img: Image.Image, export_format: str):
161
  output_img = output_img.convert("RGB")
162
+ file_id = uuid.uuid4().hex
 
 
163
 
164
+ if export_format == "PNG":
165
+ path = os.path.join(ENHANCED_DIR, f"{file_id}.png")
166
+ output_img.save(path, format="PNG", compress_level=0)
167
+ else:
168
+ path = os.path.join(ENHANCED_DIR, f"{file_id}.tiff")
169
+ output_img.save(path, format="TIFF")
170
 
171
+ return path
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
 
173
 
174
  # ---------------------------------
 
176
  # ---------------------------------
177
 
178
  @spaces.GPU
179
+ def enhance_image(
180
+ enhancer_type,
181
+ ratio_name,
182
+ tile_preset,
183
+ scale_mode,
184
+ export_format,
185
+ input_image,
186
+ ):
187
  if input_image is None:
188
+ return (
189
+ None,
190
+ None,
191
+ "No stats available yet."
192
+ )
193
 
194
+ original_img = Image.fromarray(input_image).convert("RGB")
195
+ original_width, original_height = original_img.size
196
 
197
  tile_width, tile_height = get_tile_dimensions(ratio_name, tile_preset)
198
  upscaler = get_upscaler(enhancer_type)
199
 
200
+ enhanced_img = run_scale_pipeline(
201
+ img=original_img,
202
+ upscaler=upscaler,
203
  tile_width=tile_width,
204
  tile_height=tile_height,
205
+ scale_mode=scale_mode,
206
  )
207
 
208
+ enhanced_width, enhanced_height = enhanced_img.size
209
+
210
+ output_path = save_output_image(enhanced_img, export_format)
211
+ file_size_bytes = os.path.getsize(output_path)
212
 
213
+ stats_markdown = build_stats_markdown(
214
+ original_width=original_width,
215
+ original_height=original_height,
216
+ enhanced_width=enhanced_width,
217
+ enhanced_height=enhanced_height,
218
+ file_size_bytes=file_size_bytes,
219
+ export_format=export_format,
220
+ scale_mode=scale_mode,
221
+ )
222
 
223
+ return enhanced_img, output_path, stats_markdown
224
 
225
 
226
  # ---------------------------------
227
  # UI
228
  # ---------------------------------
229
 
230
+ with gr.Blocks() as demo:
231
  gr.Markdown("# Image Enhancer")
232
 
233
+ # 1. Enhancer Type
234
  with gr.Group():
235
  enhancer_type = gr.Radio(
236
  choices=["Anime Enhancer", "Photo Enhancer"],
 
238
  label="Enhancer Type"
239
  )
240
 
241
+ # 2. Combined Tile Settings box
242
  with gr.Group():
243
  gr.Markdown("### Tile Settings")
244
 
245
  ratio_name = gr.Radio(
246
  choices=["16:9", "9:16", "4:5", "1:1", "5:4", "2:3", "3:2"],
247
+ value="1:1",
248
  label="Aspect Ratio"
249
  )
250
 
 
255
  )
256
 
257
  tile_display = gr.Markdown(
258
+ value=update_tile_display("1:1", "768")
259
  )
260
 
261
+ # Extra processing settings
262
+ with gr.Group():
263
+ gr.Markdown("### Output Settings")
264
+
265
+ scale_mode = gr.Radio(
266
+ choices=["4x", "8x Experimental", "16x Experimental"],
267
+ value="4x",
268
+ label="Scale Mode"
269
+ )
270
+
271
+ export_format = gr.Radio(
272
+ choices=["PNG", "TIFF"],
273
+ value="PNG",
274
+ label="Export Format"
275
+ )
276
+
277
+ # 3. Input Image
278
  with gr.Group():
279
  input_image = gr.Image(
280
  type="numpy",
 
282
  height=400
283
  )
284
 
285
+ run_button = gr.Button("Enhance Image")
286
 
287
+ # 4. Output Preview
288
  with gr.Group():
289
  gr.Markdown("### Output Preview")
290
+ output_preview = gr.Image(
291
+ type="pil",
292
+ label="Enhanced Preview",
293
+ height=400
294
  )
295
 
296
+ # 5. Download box
297
  with gr.Group():
298
  gr.Markdown("### New Enhanced Image File")
299
  download_file = gr.File(
300
  label="Download new enhanced image file"
301
  )
302
 
303
+ # Stats box
304
+ with gr.Group():
305
+ gr.Markdown("### Image Stats")
306
+ stats_box = gr.Markdown(
307
+ value="No stats available yet."
308
+ )
309
+
310
  ratio_name.change(
311
  fn=update_tile_display,
312
  inputs=[ratio_name, tile_preset],
 
320
  )
321
 
322
  run_button.click(
323
+ fn=enhance_image,
324
  inputs=[
325
  enhancer_type,
326
  ratio_name,
327
  tile_preset,
328
+ scale_mode,
329
+ export_format,
330
  input_image,
331
  ],
332
  outputs=[
333
+ output_preview,
334
  download_file,
335
+ stats_box,
336
  ],
337
  show_progress=True
338
  )