2ch commited on
Commit
7fe4df4
1 Parent(s): 4e9a521

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +219 -36
app.py CHANGED
@@ -1,8 +1,6 @@
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
@@ -15,6 +13,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')
@@ -22,7 +28,6 @@ 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",
@@ -50,7 +55,7 @@ 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)
@@ -61,7 +66,7 @@ def dl_textures(texture_url):
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)
@@ -72,21 +77,17 @@ def create_texture_preview(texture_folder, output_folder, size=(246, 246)):
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 {{
@@ -191,7 +192,8 @@ footer:after {
191
  }
192
 
193
  #textured_result-download-link,
194
- #restored_image-download-link {
 
195
  position: absolute;
196
  z-index: 9999;
197
  padding: 2px 4px;
@@ -203,17 +205,21 @@ footer:after {
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
@@ -238,24 +244,28 @@ const PageLoadObserver = new MutationObserver((mutationsList, observer) => {
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);
@@ -343,6 +353,12 @@ const PageLoadObserver = new MutationObserver((mutationsList, observer) => {
343
  });
344
 
345
  ImageRestoredObserver.observe(document, DownloadLinkObserverOptions);
 
 
 
 
 
 
346
 
347
  }
348
  }
@@ -352,7 +368,8 @@ const PageLoadObserver = new MutationObserver((mutationsList, observer) => {
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)
@@ -386,7 +403,6 @@ def restore_face_codeformer(img_path: str) -> None:
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):
@@ -435,7 +451,7 @@ def get_file_paths(input_path: str | list[str], extensions_list: list[str]) -> l
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]
@@ -446,10 +462,9 @@ async def restore_upscale(files, restore_method):
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):
@@ -488,17 +503,185 @@ def image_noise_softlight_layer_mix(img, texture, output: str = None, opacity: f
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"):
 
1
  import os
 
2
  import gradio as gr
3
  import shutil
 
4
  import numpy as np
5
  from PIL import Image
6
  import asyncio
 
13
  import requests
14
  from urllib.parse import quote
15
  from unicodedata import normalize
16
+ import json
17
+ import time
18
+ from html.parser import HTMLParser
19
+ try:
20
+ import cv2
21
+ except ImportError:
22
+ os.system('pip install opencv-python')
23
+ import cv2
24
 
25
  root = os.path.dirname(os.path.abspath(__file__))
26
  textures_folder = os.path.join(root, 'textures')
 
28
  valid_extensions = ['.jpeg', '.jpg', '.png']
29
 
30
 
 
31
  textures_repo = "https://huggingface.co/datasets/2ch/textures/resolve/main/"
32
  textures_for_download = [
33
  f"{textures_repo}гауссовский_шум_и_мелкое_зерно.png?download=true",
 
55
  ]
56
 
57
 
58
+ def dl_textures(texture_url: str) -> None:
59
  texture_for_download = quote(normalize('NFD', texture_url), safe='/?:=')
60
  filename = texture_url.split('/')[-1].split('?')[0]
61
  file_path = os.path.join(textures_folder, filename)
 
66
  f.write(chunk)
67
 
68
 
69
+ def create_texture_preview(texture_folder: str, output_folder: str, size: tuple[int] = (246, 246)) -> None:
70
  os.makedirs(output_folder, exist_ok=True)
71
  for texture in os.listdir(texture_folder):
72
  img_path = os.path.join(texture_folder, texture)
 
77
  cv2.imwrite(os.path.join(output_folder, texture), img)
78
 
79
 
80
+ def prepare_textures(texture_folder: str, output_folder: str) -> None:
81
  with ThreadPoolExecutor(max_workers=len(textures_for_download)) as executor:
82
  futures = [executor.submit(dl_textures, texture_for_download) for texture_for_download in
83
  textures_for_download]
84
  for future in as_completed(futures):
85
  future.result()
 
86
  create_texture_preview(texture_folder, output_folder, size=(246, 246))
87
 
88
 
 
 
 
 
89
  preview_css = ""
90
+ prepare_textures(textures_folder, os.path.join(root, 'preview'))
91
  for i, texture in enumerate(os.listdir(textures_folder), start=1):
92
  if os.path.splitext(texture)[1].lower() in valid_extensions:
93
  preview_css += f"""[data-testid="{i:02d}-radio-label"]::before {{
 
192
  }
193
 
194
  #textured_result-download-link,
195
+ #restored_image-download-link,
196
+ #upscaled_image-download-link {
197
  position: absolute;
198
  z-index: 9999;
199
  padding: 2px 4px;
 
205
  transition: 300ms
206
  }
207
 
208
+ #textured_result-download-link:hover,
209
+ #restored_image-download-link:hover,
210
+ #upscaled_image-download-link:hover {
211
  color: #99f7a8
212
  }
213
 
214
+ #restored_images.disabled,
215
+ #upscaled_images.disabled {
216
  height: 0px !important;
217
  opacity: 0;
218
  transition: 300ms
219
  }
220
 
221
+ #restored_images.enabled,
222
+ #upscaled_images.enabled {
223
  transition: 300ms
224
  }
225
  """ + preview_css
 
244
  document.querySelector("label[data-testid='05-radio-label']").click()
245
  }, 150);
246
  })
247
+ function checkImagesAndSetClass(galleryElement) {
248
+ const firstDiv = galleryElement.querySelector('div:first-child');
 
249
  const hasChildElements = firstDiv && firstDiv.children.length > 0;
250
+ const hasImages = galleryElement.querySelectorAll('img').length > 0;
251
  if (hasChildElements || hasImages) {
252
+ galleryElement.classList.add('enabled');
253
+ galleryElement.classList.remove('disabled');
254
  } else {
255
+ galleryElement.classList.add('disabled');
256
+ galleryElement.classList.remove('enabled');
257
  }
258
  }
259
+ function setupGalleryObserver(galleryId) {
260
+ let gallery = document.getElementById(galleryId);
261
+ const observer = new MutationObserver(() => {
262
+ checkImagesAndSetClass(gallery);
263
+ });
264
+ observer.observe(gallery, { childList: true, subtree: true });
265
+ checkImagesAndSetClass(gallery);
266
+ }
267
+ setupGalleryObserver('restored_images');
268
+ setupGalleryObserver('upscaled_images');
269
  function magnify(imgID, zoom) {
270
  var img, glass, w, h, bw;
271
  img = document.querySelector(imgID);
 
353
  });
354
 
355
  ImageRestoredObserver.observe(document, DownloadLinkObserverOptions);
356
+
357
+ const ImageUpscaledObserver = new MutationObserver((mutationsList, observer) => {
358
+ DownloadLinkObserverCallback(mutationsList, observer, '#upscaled_images img[data-testid="detailed-image"]', '#upscaled_image-download-link', 'upscaled_image-download-link');
359
+ });
360
+
361
+ ImageUpscaledObserver.observe(document, DownloadLinkObserverOptions);
362
 
363
  }
364
  }
 
368
  PageLoadObserver.observe(document, { childList: true, subtree: true });
369
  """
370
 
371
+
372
+ def extract_path_from_result(predict_answer: str | list[str] | tuple[str]) -> str:
373
  if isinstance(predict_answer, (tuple, list)):
374
  result = predict_answer[0]
375
  shutil.rmtree(os.path.dirname(predict_answer[1]), ignore_errors=True)
 
403
  restore_face_common(img_path, result, "codeformer")
404
 
405
 
 
406
  async def restore_faces_one_image(img_path: str, func_list: list) -> bool:
407
  def run_func(func) -> bool:
408
  for _ in range(3):
 
451
  return files
452
 
453
 
454
+ async def restore_upscale(files: tuple, restore_method: str) -> list[str]:
455
  file_paths = [file.name for file in files]
456
  if restore_method == 'codeformer':
457
  func_list = [restore_face_codeformer]
 
462
  results = await restore_faces_batch(file_paths, func_list, batch_size=3)
463
  if results:
464
  file_paths = get_file_paths(file_paths, valid_extensions)
 
465
  return file_paths
466
  else:
467
+ return ['https://iili.io/JzrxjDP.png']
468
 
469
 
470
  def image_noise_softlight_layer_mix(img, texture, output: str = None, opacity: float = 0.7):
 
503
  return np.array(image)
504
 
505
 
506
+ def apply_texture(input_image, textures_choice: str, opacity_slider: float):
507
  result = image_noise_softlight_layer_mix(input_image, os.path.join(textures_folder, textures_choice), opacity=opacity_slider)
508
  return [result]
509
 
510
 
511
+ def temp_upload_file(file_path: str) -> str | None:
512
+ servers = [
513
+ ('https://transfer.sh/', 'fileToUpload'),
514
+ ('https://x0.at/', 'file'),
515
+ ('https://tmpfiles.org/api/v1/upload', 'file'),
516
+ ('https://uguu.se/upload.php', 'files[]')
517
+ ]
518
+ for i in range(3):
519
+ for server, file_key in servers:
520
+ try:
521
+ with open(file_path, 'rb') as f:
522
+ files = {file_key: f}
523
+ response = requests.post(server, files=files)
524
+ if response.status_code == 200:
525
+ if server == 'https://transfer.sh/':
526
+ return response.text.replace("https://transfer.sh/","https://transfer.sh/get/").replace("\n","")
527
+ elif server == 'https://tmpfiles.org/api/v1/upload':
528
+ response_json = response.json()
529
+ if response_json['status'] == 'success':
530
+ return response_json['data']['url'].replace("https://tmpfiles.org/", "https://tmpfiles.org/dl/")
531
+ elif server == 'https://uguu.se/upload.php':
532
+ response_json = response.json()
533
+ if response_json['success']:
534
+ return response_json['files'][0]['url']
535
+ else:
536
+ return response.text
537
+ except Exception as e:
538
+ print(f'{server}: {e}')
539
+ return None
540
+
541
+
542
+ def upload_image(image_path: str) -> str | None:
543
+ files = {'source': open(image_path, "rb")}
544
+ data = {'key': '6d207e02198a847aa98d0a2a901485a5', 'action': 'upload', 'format': 'json'}
545
+ response = requests.post('https://freeimage.host/api/1/upload', files=files, data=data)
546
+ if response.json()["status_code"] == 200:
547
+ return response.json()["image"]["url"]
548
+ else:
549
+ return temp_upload_file(image_path)
550
+
551
+
552
+ def get_headers(url: str) -> dict:
553
+ session = requests.Session()
554
+ anon_auth = session.get(url)
555
+ cookies = session.cookies.get_dict()
556
+ return {
557
+ 'content-type': 'application/json',
558
+ 'cookie': f'csrftoken={cookies["csrftoken"]}; replicate_anonymous_id={cookies["replicate_anonymous_id"]};',
559
+ 'origin': 'https://replicate.com',
560
+ 'x-csrftoken': cookies['csrftoken'],
561
+ 'authority': 'replicate.com',
562
+ 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/jxl,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
563
+ 'accept-language': 'ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7',
564
+ 'cache-control': 'no-cache',
565
+ 'dnt': '1',
566
+ 'pragma': 'no-cache',
567
+ 'referer': f'{url}?input=http',
568
+ 'sec-ch-ua': '"Chromium";v="117", "Not;A=Brand";v="8"',
569
+ 'sec-ch-ua-mobile': '?0',
570
+ 'sec-ch-ua-platform': '"Windows"',
571
+ 'sec-fetch-dest': 'document',
572
+ 'sec-fetch-mode': 'navigate',
573
+ 'sec-fetch-site': 'same-origin',
574
+ 'sec-fetch-user': '?1',
575
+ 'upgrade-insecure-requests': '1',
576
+ 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36'
577
+ }
578
+
579
+
580
+ def get_version(url: str) -> str:
581
+ url = url.rstrip('/') + '/versions'
582
+ response = requests.get(url)
583
+
584
+ class Version(HTMLParser):
585
+ def __init__(self):
586
+ super().__init__()
587
+ self.recording = 0
588
+ self.data = ''
589
+
590
+ def handle_starttag(self, tag, attrs):
591
+ if tag == 'a':
592
+ for name, value in attrs:
593
+ if name == 'href' and '/versions/' in value:
594
+ self.recording = 1
595
+
596
+ def handle_endtag(self, tag):
597
+ if tag == 'a' and self.recording:
598
+ self.recording -= 1
599
+
600
+ def handle_data(self, data):
601
+ if self.recording:
602
+ self.data = data
603
+
604
+ parser = Version()
605
+ parser.feed(response.text)
606
+ return parser.data.strip()
607
+
608
+
609
+ def replicate_upscale(url: str, image_url: str, upscale: int = 2) -> str:
610
+ version = get_version(url)
611
+ headers = get_headers(url)
612
+ session = requests.Session()
613
+ anon_auth = session.get(url, headers=headers)
614
+ data = {
615
+ "version": version,
616
+ "input": {
617
+ "img": image_url,
618
+ "image": image_url,
619
+ "upscale": upscale,
620
+ "scale": upscale,
621
+ "version": "General - RealESRGANplus",
622
+ },
623
+ "face_enhance": False,
624
+ "is_training": False,
625
+ "stream": False
626
+ }
627
+ response = session.post('https://replicate.com/api/predictions', headers=headers, data=json.dumps(data))
628
+ prediction_id = response.json()['id']
629
+ while True:
630
+ response = session.get(f'https://replicate.com/api/predictions/{prediction_id}', headers=headers)
631
+ if 'status' in response.json():
632
+ status = response.json()['status']
633
+ else:
634
+ status = 'processing'
635
+ if status == 'succeeded':
636
+ break
637
+ time.sleep(1)
638
+ session.close()
639
+ return response.json()['output']
640
+
641
+
642
+ def upscaler(img_url: str) -> list[str] | None:
643
+ def run(url):
644
+ try:
645
+ return replicate_upscale(url, img_url)
646
+ except Exception as e:
647
+ print(e)
648
+ return None
649
+
650
+ urls = [
651
+ 'https://replicate.com/cjwbw/real-esrgan',
652
+ 'https://replicate.com/daanelson/real-esrgan-a100',
653
+ 'https://replicate.com/xinntao/realesrgan',
654
+ ]
655
+ with ThreadPoolExecutor() as executor:
656
+ futures = {executor.submit(run, url) for url in urls}
657
+ for future in as_completed(futures):
658
+ result = future.result()
659
+ if result is not None:
660
+ break
661
+ return [result]
662
+
663
+
664
+ def check_upscale_result(image: str) -> list[str]:
665
+ attempt = 0
666
+ response = None
667
+ while attempt < 3:
668
+ response = upscaler(upload_image(image))
669
+ if response:
670
+ return response
671
+ attempt += 1
672
+ return ['https://iili.io/JzrxjDP.png']
673
+
674
+
675
+ with gr.Blocks(analytics_enabled=False, css=radio_css, theme='Taithrah/Minimal', title='апскейл') as demo:
676
+ with gr.Tab(label="апскейл", elem_id="upscale_tab"):
677
+ 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="upscaled_images")
678
+ upload_button = gr.UploadButton("выбор одного изображения для обработки", file_types=["image"], file_count="single", variant="primary")
679
+ upload_button.upload(fn=check_upscale_result, inputs=[upload_button], outputs=file_output, api_name="upscale")
680
  with gr.Tab(label="восстановление лиц", id=1, elem_id="restore_tab"):
681
  restore_method = gr.Radio(["codeformer", "gfpgan", "оба"], value="codeformer", label="", interactive=True)
682
  restore_method.change(fn=lambda x: print(f"restore_method value = {x}"), inputs=restore_method, api_name="show_selected_method")
683
  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")
684
+ upload_button = gr.UploadButton("выбор нескольких изображений для обработки", file_types=["image"], file_count="multiple", variant="primary")
685
  upload_button.upload(fn=restore_upscale, inputs=[upload_button, restore_method], outputs=file_output, api_name="face_restore")
686
  with gr.Tab(label="наложение зернистости пленки и шума", id=2, elem_id="textures_tab"):
687
  with gr.Row(variant="compact", elem_id="textures_tab_images"):