2ch commited on
Commit
859e4b0
·
1 Parent(s): 47f35fe

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +542 -0
app.py ADDED
@@ -0,0 +1,542 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ os.system('pip install numpy pillow opencv-python fastapi starlette uvicorn requests')
3
+ import gradio as gr
4
+ import shutil
5
+ import cv2
6
+ import numpy as np
7
+ from PIL import Image
8
+ import asyncio
9
+ from concurrent.futures import ThreadPoolExecutor, as_completed
10
+ from gradio_client import Client
11
+ from fastapi import FastAPI, Request
12
+ from fastapi.staticfiles import StaticFiles
13
+ from starlette.responses import Response
14
+ import uvicorn
15
+ import requests
16
+ from urllib.parse import quote
17
+ from unicodedata import normalize
18
+
19
+ root = os.path.dirname(os.path.abspath(__file__))
20
+ textures_folder = os.path.join(root, 'textures')
21
+ os.makedirs(textures_folder, exist_ok=True)
22
+ valid_extensions = ['.jpeg', '.jpg', '.png']
23
+
24
+
25
+
26
+ textures_repo = "https://huggingface.co/datasets/2ch/textures/resolve/main/"
27
+ textures_for_download = [
28
+ f"{textures_repo}гауссовский_шум_и_мелкое_зерно.png?download=true",
29
+ f"{textures_repo}грязная_матрица.png?download=true",
30
+ f"{textures_repo}для_ночных_и_тёмных_кадров_сильный_шум_и_пыль.png?download=true",
31
+ f"{textures_repo}для_ночных_и_тёмных_кадров_царапины_шум_пыль_дымка.png?download=true",
32
+ f"{textures_repo}для_светлых_и_солнечных_ярких_фото_мелкое_констрастное_зерно.png?download=true",
33
+ f"{textures_repo}зернистость_плёнки.png?download=true",
34
+ f"{textures_repo}зернистость_плёнки_с_грязью.png?download=true",
35
+ f"{textures_repo}испорченная_ворсом_плёнка.png?download=true",
36
+ f"{textures_repo}мелкий_цветной_шум.png?download=true",
37
+ f"{textures_repo}мелкое_контрастное_зерно_и_средний_цветвой_шум.png?download=true",
38
+ f"{textures_repo}очень_мелкое_зерно.png?download=true",
39
+ f"{textures_repo}пыльная_плёнка.png?download=true",
40
+ f"{textures_repo}сильный_цветовой_шум_для_ночных_фото.png?download=true",
41
+ f"{textures_repo}слабый_естественный_шум_матрицы_смартфона.png?download=true",
42
+ f"{textures_repo}среднее_зерно.png?download=true",
43
+ f"{textures_repo}среднее_монохромное_зерно_пыль_и_ворсинки.png?download=true",
44
+ f"{textures_repo}средний_цветной_шум.png?download=true",
45
+ f"{textures_repo}старая_матрица.png?download=true",
46
+ f"{textures_repo}старая_потёртая_плёнка.png?download=true",
47
+ f"{textures_repo}цветной_шум_матрицы.png?download=true",
48
+ f"{textures_repo}цветной_шум_на_плёнке.png?download=true",
49
+ f"{textures_repo}шумная_матрица.png?download=true",
50
+ ]
51
+
52
+
53
+ def dl_textures(texture_url):
54
+ texture_for_download = quote(normalize('NFD', texture_url), safe='/?:=')
55
+ filename = texture_url.split('/')[-1].split('?')[0]
56
+ file_path = os.path.join(textures_folder, filename)
57
+ response = requests.get(texture_for_download, stream=True)
58
+ response.raise_for_status()
59
+ with open(file_path, 'wb') as f:
60
+ for chunk in response.iter_content(chunk_size=8192):
61
+ f.write(chunk)
62
+
63
+
64
+ def create_texture_preview(texture_folder, output_folder, size=(246, 246)):
65
+ os.makedirs(output_folder, exist_ok=True)
66
+ for texture in os.listdir(texture_folder):
67
+ img_path = os.path.join(texture_folder, texture)
68
+ img = cv2.imread(img_path, cv2.IMREAD_UNCHANGED)
69
+ start_x = np.random.randint(0, img.shape[1] - size[1])
70
+ start_y = np.random.randint(0, img.shape[0] - size[0])
71
+ img = img[start_y:start_y + size[0], start_x:start_x + size[1]]
72
+ cv2.imwrite(os.path.join(output_folder, texture), img)
73
+
74
+
75
+ def prepare_textures(texture_folder, output_folder):
76
+ with ThreadPoolExecutor(max_workers=len(textures_for_download)) as executor:
77
+ futures = [executor.submit(dl_textures, texture_for_download) for texture_for_download in
78
+ textures_for_download]
79
+ for future in as_completed(futures):
80
+ future.result()
81
+
82
+ create_texture_preview(texture_folder, output_folder, size=(246, 246))
83
+
84
+
85
+ prepare_textures(textures_folder, os.path.join(root, 'preview'))
86
+
87
+
88
+
89
+ preview_css = ""
90
+ for i, texture in enumerate(os.listdir(textures_folder), start=1):
91
+ if os.path.splitext(texture)[1].lower() in valid_extensions:
92
+ preview_css += f"""[data-testid="{i:02d}-radio-label"]::before {{
93
+ background-color: transparent !important;
94
+ background-image: url("./preview/{texture}") !important;
95
+ }}\n"""
96
+
97
+ radio_css = """
98
+ html,
99
+ body {
100
+ background: var(--body-background-fill);
101
+ }
102
+
103
+ .gradio-container {
104
+ max-width: 1396px !important;
105
+ }
106
+
107
+ #textures label {
108
+ position: relative;
109
+ width: 256px;
110
+ height: 256px;
111
+ display: flex;
112
+ flex-direction: row;
113
+ align-items: flex-end;
114
+ background: none !important;
115
+ padding: 4px !important;
116
+ transition: .3s;
117
+ }
118
+
119
+ #textures label::before {
120
+ width: 246px;
121
+ height: 246px;
122
+ border-radius: 8px;
123
+ display: block;
124
+ content: "";
125
+ transition: .3s;
126
+ background: red;
127
+ position: relative;
128
+ top: 0px;
129
+ }
130
+
131
+ #textures label:hover::before,
132
+ #textures label:active::before,
133
+ #textures label.selected::before {
134
+ mix-blend-mode: soft-light;
135
+ transition: .3s
136
+ }
137
+
138
+ #textures span:not([data-testid="block-info"]),
139
+ #textures input {
140
+ position: absolute;
141
+ z-index: 999;
142
+ }
143
+
144
+ #textures input {
145
+ position: absolute;
146
+ z-index: 999;
147
+ bottom: 9px;
148
+ left: 9px;
149
+ }
150
+
151
+ #textures span:not([data-testid="block-info"]) {
152
+ left: 21px;
153
+ padding: 2px 8px;
154
+ background: rgba(0, 0, 0, .57);
155
+ backdrop-filter: blur(3px)
156
+ }
157
+
158
+ #textures {
159
+ background-color: hsla(0, 0%, 50%, 1);
160
+ }
161
+
162
+ .built-with,
163
+ .show-api,
164
+ footer .svelte-mpyp5e {
165
+ display: none !important;
166
+ }
167
+
168
+ footer:after {
169
+ content: "ну пролапс, ну и что?";
170
+ }
171
+
172
+ #zoom {
173
+ position: absolute;
174
+ top: 50%;
175
+ left: 50%;
176
+ width: 250px;
177
+ height: 250px;
178
+ background-repeat: no-repeat;
179
+ box-shadow: 0px 0px 10px 5px rgba(0, 0, 0, .2);
180
+ border-radius: 50%;
181
+ cursor: none;
182
+ pointer-events: none;
183
+ z-index: 999;
184
+ opacity: 0;
185
+ transform: scale(0);
186
+ transition: opacity 500ms, transform 500ms;
187
+ }
188
+
189
+ #textures_tab .image-button {
190
+ cursor: none;
191
+ }
192
+
193
+ #textured_result-download-link,
194
+ #restored_image-download-link {
195
+ position: absolute;
196
+ z-index: 9999;
197
+ padding: 2px 4px;
198
+ margin: 0 7px;
199
+ background: black;
200
+ bottom: 0;
201
+ right: 0;
202
+ font-size: 20px;
203
+ transition: 300ms
204
+ }
205
+
206
+ #download-link:hover {
207
+ color: #99f7a8
208
+ }
209
+
210
+ #restored_images.disabled {
211
+ height: 0px !important;
212
+ opacity: 0;
213
+ transition: 300ms
214
+ }
215
+
216
+ #restored_images.enabled {
217
+ transition: 300ms
218
+ }
219
+ """ + preview_css
220
+
221
+ custom_js = """
222
+ const PageLoadObserver = new MutationObserver((mutationsList, observer) => {
223
+ for (let mutation of mutationsList) {
224
+ if (mutation.type === 'childList') {
225
+ const tabsDiv = document.querySelector('div.tab-nav');
226
+ if (tabsDiv) {
227
+ observer.disconnect();
228
+ document.querySelector('#textures_tab-button').addEventListener('click', () => {
229
+ setTimeout(() => {
230
+ let labels = document.querySelectorAll('label[data-testid]');
231
+ labels.forEach((label) => {
232
+ let input = label.querySelector('input[type="radio"]');
233
+ if (input) {
234
+ let title = input.value.split('.')[0].replace(/_/g, ' ');
235
+ label.title = title;
236
+ }
237
+ });
238
+ document.querySelector("label[data-testid='05-radio-label']").click()
239
+ }, 150);
240
+ })
241
+ let RestoredGallery = document.getElementById('restored_images');
242
+ function checkImagesAndSetClass() {
243
+ const firstDiv = RestoredGallery.querySelector('div:first-child');
244
+ const hasChildElements = firstDiv && firstDiv.children.length > 0;
245
+ const hasImages = RestoredGallery.querySelectorAll('img').length > 0;
246
+ if (hasChildElements || hasImages) {
247
+ RestoredGallery.classList.add('enabled');
248
+ RestoredGallery.classList.remove('disabled');
249
+ } else {
250
+ RestoredGallery.classList.add('disabled');
251
+ RestoredGallery.classList.remove('enabled');
252
+ }
253
+ }
254
+ const FaceResoreResultCheck = new MutationObserver((mutations) => {
255
+ checkImagesAndSetClass();
256
+ });
257
+ FaceResoreResultCheck.observe(RestoredGallery, {childList: true, subtree: true});
258
+ checkImagesAndSetClass();
259
+ function magnify(imgID, zoom) {
260
+ var img, glass, w, h, bw;
261
+ img = document.querySelector(imgID);
262
+ glass = document.createElement("DIV");
263
+ glass.setAttribute("id", "zoom");
264
+ img.parentElement.insertBefore(glass, img);
265
+ glass.style.backgroundImage = "url('" + img.src + "')";
266
+ glass.style.backgroundRepeat = "no-repeat";
267
+ glass.style.backgroundSize = (img.width * zoom) + "px " + (img.height * zoom) + "px";
268
+ bw = 3;
269
+ w = glass.offsetWidth / 2;
270
+ h = glass.offsetHeight / 2;
271
+ glass.addEventListener("mousemove", moveMagnifier);
272
+ img.addEventListener("mousemove", moveMagnifier);
273
+ glass.addEventListener("touchmove", moveMagnifier);
274
+ img.addEventListener("touchmove", moveMagnifier);
275
+ function moveMagnifier(e) {
276
+ var pos, x, y;
277
+ e.preventDefault();
278
+ pos = getCursorPos(e);
279
+ x = pos.x;
280
+ y = pos.y;
281
+ if (x > img.width - (w / zoom)) { x = img.width - (w / zoom); }
282
+ if (x < w / zoom) { x = w / zoom; }
283
+ if (y > img.height - (h / zoom)) { y = img.height - (h / zoom); }
284
+ if (y < h / zoom) { y = h / zoom; }
285
+ glass.style.left = (x - w) + "px";
286
+ glass.style.top = (y - h) + "px";
287
+ glass.style.backgroundPosition = "-" + ((x * zoom) - w + bw) + "px -" + ((y * zoom) - h) + "px";
288
+ glass.style.backgroundImage = "url('" + img.src + "')";
289
+ }
290
+ function getCursorPos(e) {
291
+ var a, x = 0, y = 0;
292
+ e = e || window.event;
293
+ a = img.getBoundingClientRect();
294
+ x = e.pageX - a.left;
295
+ y = e.pageY - a.top;
296
+ x = x - window.scrollX;
297
+ y = y - window.scrollY;
298
+ return { x: x, y: y };
299
+ }
300
+
301
+ img.addEventListener("mouseover", function () {
302
+
303
+ glass.style.opacity = "1";
304
+ glass.style.transform = "scale(1)";
305
+ });
306
+ img.addEventListener("mouseout", function () {
307
+ glass.style.opacity = "0";
308
+ glass.style.transform = "scale(0)";
309
+ });
310
+ }
311
+ function setupDownloadLink(imgSelector, linkSelector, linkId, magnifyImage) {
312
+ const imgElement = document.querySelector(imgSelector);
313
+ if (imgElement && imgElement.src) {
314
+ let downloadLink = document.querySelector(linkSelector);
315
+ if (!downloadLink) {
316
+ if (magnifyImage) {
317
+ magnify(magnifyImage, 3);
318
+ }
319
+ downloadLink = document.createElement('a');
320
+ downloadLink.id = linkId;
321
+ downloadLink.innerText = 'скачать';
322
+ imgElement.after(downloadLink);
323
+ }
324
+ downloadLink.href = imgElement.src;
325
+ downloadLink.download = '';
326
+ }
327
+ }
328
+
329
+ const DownloadLinkObserverCallback = (mutationsList, observer, imgSelector, linkSelector, linkId, magnifyImage) => {
330
+ setupDownloadLink(imgSelector, linkSelector, linkId, magnifyImage);
331
+ };
332
+
333
+ const DownloadLinkObserverOptions = { childList: true, subtree: true, attributes: true, attributeFilter: ['src'] };
334
+
335
+ const ImageTexturedObserver = new MutationObserver((mutationsList, observer) => {
336
+ DownloadLinkObserverCallback(mutationsList, observer, '#textured_result img[data-testid="detailed-image"]', '#textured_result-download-link', 'textured_result-download-link', "#textured_result .image-button img");
337
+ });
338
+
339
+ ImageTexturedObserver.observe(document, DownloadLinkObserverOptions);
340
+
341
+ const ImageRestoredObserver = new MutationObserver((mutationsList, observer) => {
342
+ DownloadLinkObserverCallback(mutationsList, observer, '#restored_images img[data-testid="detailed-image"]', '#restored_image-download-link', 'restored_image-download-link');
343
+ });
344
+
345
+ ImageRestoredObserver.observe(document, DownloadLinkObserverOptions);
346
+
347
+ }
348
+ }
349
+ }
350
+ });
351
+
352
+ PageLoadObserver.observe(document, { childList: true, subtree: true });
353
+ """
354
+
355
+ def extract_path_from_result(predict_answer):
356
+ if isinstance(predict_answer, (tuple, list)):
357
+ result = predict_answer[0]
358
+ shutil.rmtree(os.path.dirname(predict_answer[1]), ignore_errors=True)
359
+ else:
360
+ result = predict_answer
361
+ return result
362
+
363
+
364
+ def restore_face_common(img_path: str, predict_answer: str, model: str) -> None:
365
+ result = extract_path_from_result(predict_answer)
366
+ if os.path.exists(result):
367
+ if os.path.exists(img_path):
368
+ os.unlink(img_path)
369
+ new_file, new_extension = os.path.splitext(result)
370
+ old_file, old_extension = os.path.splitext(img_path)
371
+ old_filename = os.path.basename(old_file)
372
+ new_location = os.path.join(os.path.dirname(img_path), f"{old_filename}_{model}{new_extension}")
373
+ shutil.move(result, new_location)
374
+ shutil.rmtree(os.path.dirname(result), ignore_errors=True)
375
+
376
+
377
+ def restore_face_gfpgan(img_path: str) -> None:
378
+ client = Client(src="https://xintao-gfpgan.hf.space/", verbose=False)
379
+ result = client.predict(img_path, "v1.4", 4, api_name="/predict")
380
+ restore_face_common(img_path, result, "gfpgan")
381
+
382
+
383
+ def restore_face_codeformer(img_path: str) -> None:
384
+ client = Client(src="https://sczhou-codeformer.hf.space/", verbose=False)
385
+ result = client.predict(img_path, True, True, True, 2, 0, api_name="/predict")
386
+ restore_face_common(img_path, result, "codeformer")
387
+
388
+
389
+
390
+ async def restore_faces_one_image(img_path: str, func_list: list) -> bool:
391
+ def run_func(func) -> bool:
392
+ for _ in range(3):
393
+ try:
394
+ func(img_path)
395
+ return True
396
+ except Exception as e:
397
+ print(f"ошибка в {func.__name__}: {e}")
398
+ return False
399
+
400
+ loop = asyncio.get_event_loop()
401
+ with ThreadPoolExecutor(max_workers=len(func_list)) as executor:
402
+ futures = [loop.run_in_executor(executor, run_func, func) for func in func_list]
403
+ results = await asyncio.gather(*futures)
404
+ return any(results)
405
+
406
+
407
+ async def restore_faces_batch(input_images: list[str], func_list: list, batch_size: int = 3) -> bool:
408
+ results = False
409
+ try:
410
+ batches = [input_images[i:i + batch_size] for i in range(0, len(input_images), batch_size)]
411
+ for batch in batches:
412
+ tasks = [restore_faces_one_image(img_path, func_list) for img_path in batch]
413
+ results = await asyncio.gather(*tasks)
414
+ return any(results)
415
+ except Exception as error:
416
+ print(error)
417
+ return results
418
+
419
+
420
+ def get_file_paths(input_path: str | list[str], extensions_list: list[str]) -> list[str]:
421
+ files = []
422
+
423
+ def add_files_from_directory(directory):
424
+ for file_name in os.listdir(directory):
425
+ if os.path.splitext(file_name)[1] in extensions_list:
426
+ files.append(os.path.abspath(os.path.join(directory, file_name)))
427
+
428
+ if isinstance(input_path, list):
429
+ for file_path in input_path:
430
+ parent_directory = os.path.dirname(file_path)
431
+ add_files_from_directory(parent_directory)
432
+ else:
433
+ add_files_from_directory(input_path)
434
+
435
+ return files
436
+
437
+
438
+ async def restore_upscale(files, restore_method):
439
+ file_paths = [file.name for file in files]
440
+ if restore_method == 'codeformer':
441
+ func_list = [restore_face_codeformer]
442
+ elif restore_method == 'gfpgan':
443
+ func_list = [restore_face_gfpgan]
444
+ else:
445
+ func_list = [restore_face_codeformer, restore_face_gfpgan]
446
+ results = await restore_faces_batch(file_paths, func_list, batch_size=3)
447
+ if results:
448
+ file_paths = get_file_paths(file_paths, valid_extensions)
449
+ print(f"restore_upscale: get_file_paths: {file_paths}")
450
+ return file_paths
451
+ else:
452
+ return [os.path.join(root, 'error.png')]
453
+
454
+
455
+ def image_noise_softlight_layer_mix(img, texture, output: str = None, opacity: float = 0.7):
456
+ if isinstance(img, Image.Image):
457
+ img = np.array(img).astype(float)
458
+ elif isinstance(img, np.ndarray):
459
+ img = img.astype(float)
460
+
461
+ if img.shape[2] == 3 and not isinstance(img, Image.Image):
462
+ img = cv2.cvtColor(img.astype(np.uint8), cv2.COLOR_RGB2BGR).astype(float)
463
+
464
+ overlay = cv2.imread(texture, cv2.IMREAD_UNCHANGED).astype(float)
465
+ start_x = np.random.randint(0, overlay.shape[1] - img.shape[1])
466
+ start_y = np.random.randint(0, overlay.shape[0] - img.shape[0])
467
+ overlay = overlay[start_y:start_y + img.shape[0], start_x:start_x + img.shape[1]]
468
+ if img.shape[2] == 3:
469
+ img = cv2.cvtColor(img.astype(np.uint8), cv2.COLOR_RGB2RGBA).astype(float)
470
+ if overlay.shape[2] == 3:
471
+ overlay = cv2.cvtColor(overlay.astype(np.uint8), cv2.COLOR_RGB2RGBA).astype(float)
472
+ overlay[..., 3] *= opacity
473
+ img_in_norm = img / 255.0
474
+ img_layer_norm = overlay / 255.0
475
+ comp_alpha = np.minimum(img_in_norm[:, :, 3], img_layer_norm[:, :, 3]) * 1.0
476
+ new_alpha = img_in_norm[:, :, 3] + (1.0 - img_in_norm[:, :, 3]) * comp_alpha
477
+ np.seterr(divide='ignore', invalid='ignore')
478
+ ratio = comp_alpha / new_alpha
479
+ ratio[ratio == np.NAN] = 0.0
480
+ comp = (1.0 - img_in_norm[:, :, :3]) * img_in_norm[:, :, :3] * img_layer_norm[:, :, :3] + img_in_norm[:, :, :3] * (
481
+ 1.0 - (1.0 - img_in_norm[:, :, :3]) * (1.0 - img_layer_norm[:, :, :3]))
482
+ ratio_rs = np.reshape(np.repeat(ratio, 3), [comp.shape[0], comp.shape[1], comp.shape[2]])
483
+ img_out = comp * ratio_rs + img_in_norm[:, :, :3] * (1.0 - ratio_rs)
484
+ img_out = np.nan_to_num(np.dstack((img_out, img_in_norm[:, :, 3])))
485
+ result = img_out * 255.0
486
+ rgb_image = cv2.cvtColor(result.astype(np.uint8), cv2.COLOR_BGR2RGB)
487
+ image = Image.fromarray(rgb_image)
488
+ return np.array(image)
489
+
490
+
491
+ def apply_texture(input_image, textures_choice, opacity_slider):
492
+ result = image_noise_softlight_layer_mix(input_image, os.path.join(textures_folder, textures_choice), opacity=opacity_slider)
493
+ return [result]
494
+
495
+
496
+ with gr.Blocks(analytics_enabled=False, css=radio_css) as demo:
497
+ with gr.Tab(label="восстановление лиц", id=1, elem_id="restore_tab"):
498
+ restore_method = gr.Radio(["codeformer", "gfpgan", "оба"], value="codeformer", label="", interactive=True)
499
+ restore_method.change(fn=lambda x: print(f"restore_method value = {x}"), inputs=restore_method, api_name="show_selected_method")
500
+ file_output = gr.Gallery(label="", container=True, object_fit="cover", columns=4, rows=4, allow_preview=True, preview=True, show_share_button=False, show_download_button=False, elem_id="restored_images")
501
+ upload_button = gr.UploadButton("выбор изображений для обработки", file_types=["image"], file_count="multiple", variant="primary")
502
+ upload_button.upload(fn=restore_upscale, inputs=[upload_button, restore_method], outputs=file_output, api_name="face_restore")
503
+ with gr.Tab(label="наложение зернистости пленки и шума", id=2, elem_id="textures_tab"):
504
+ with gr.Row(variant="compact", elem_id="textures_tab_images"):
505
+ input_image = gr.Image(label="исходник", sources=["upload", "clipboard"], type="numpy")
506
+ result_image = gr.Gallery(label="результат", elem_id="textured_result", allow_preview=True, preview=True, show_share_button=False, show_download_button=False)
507
+ opacity_slider = gr.Slider(minimum=0.1, maximum=1.0, value=0.7, step=0.1, label="видимость")
508
+ apply_button = gr.Button(value="применить", variant="primary")
509
+ texture_files = [(f"{i:02d}", texture) for i, texture in enumerate(os.listdir(textures_folder), start=1) if os.path.splitext(texture)[1].lower() in valid_extensions]
510
+ textures_choice = gr.Radio(texture_files, show_label=False, interactive=True, elem_id="textures")
511
+ apply_button.click(fn=apply_texture, inputs=[input_image, textures_choice, opacity_slider], outputs=result_image, api_name="texturize")
512
+
513
+
514
+ app = FastAPI()
515
+
516
+
517
+ @app.middleware("http")
518
+ async def some_fastapi_middleware(request: Request, call_next):
519
+ response = await call_next(request)
520
+ path = request.url.path
521
+ if path == "/":
522
+ response_body = ""
523
+ async for chunk in response.body_iterator:
524
+ response_body += chunk.decode()
525
+ javascript = f"""
526
+ <script type="text/javascript">
527
+ {custom_js}
528
+ </script>
529
+ """
530
+ response_body = response_body.replace("</body>", javascript + "</body>")
531
+ del response.headers["content-length"]
532
+ return Response(
533
+ content=response_body,
534
+ status_code=response.status_code,
535
+ headers=dict(response.headers),
536
+ media_type=response.media_type
537
+ )
538
+ return response
539
+
540
+ app.mount("/preview", StaticFiles(directory=os.path.join(root, 'preview')), name="preview")
541
+ gr.mount_gradio_app(app, demo, path="/")
542
+ uvicorn.run(app, host="0.0.0.0", port=7860)