HariLogicgo commited on
Commit
58c492c
·
1 Parent(s): b62f0e7
Files changed (6) hide show
  1. .DS_Store +0 -0
  2. app.py +1301 -0
  3. packages.txt +3 -0
  4. requirements.txt +22 -0
  5. utils/dataops.py +168 -0
  6. webui.bat +162 -0
.DS_Store ADDED
Binary file (6.15 kB). View file
 
app.py ADDED
@@ -0,0 +1,1301 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gc
3
+ import re
4
+ import cv2
5
+ import numpy as np
6
+ import gradio as gr
7
+ import torch
8
+ import traceback
9
+ import math
10
+ import time
11
+ import ast
12
+ import argparse
13
+ import zipfile
14
+ from collections import defaultdict
15
+ from facexlib.utils.misc import download_from_url
16
+ from basicsr.utils.realesrganer import RealESRGANer
17
+ from utils.dataops import auto_split_upscale
18
+
19
+ input_images_limit = 5
20
+ # Define URLs and their corresponding local storage paths
21
+ face_models = {
22
+ "GFPGANv1.4.pth" : ["https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.4.pth",
23
+ "https://github.com/TencentARC/GFPGAN/",
24
+ """GFPGAN: Towards Real-World Blind Face Restoration and Upscalling of the image with a Generative Facial Prior.
25
+ GFPGAN aims at developing a Practical Algorithm for Real-world Face Restoration.
26
+ It leverages rich and diverse priors encapsulated in a pretrained face GAN (e.g., StyleGAN2) for blind face restoration."""],
27
+
28
+ "RestoreFormer++.ckpt": ["https://github.com/wzhouxiff/RestoreFormerPlusPlus/releases/download/v1.0.0/RestoreFormer++.ckpt",
29
+ "https://github.com/wzhouxiff/RestoreFormerPlusPlus",
30
+ """RestoreFormer++: Towards Real-World Blind Face Restoration from Undegraded Key-Value Pairs.
31
+ RestoreFormer++ is an extension of RestoreFormer. It proposes to restore a degraded face image with both fidelity and \
32
+ realness by using the powerful fully-spacial attention mechanisms to model the abundant contextual information in the face and \
33
+ its interplay with reconstruction-oriented high-quality priors."""],
34
+
35
+ "CodeFormer.pth" : ["https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/codeformer.pth",
36
+ "https://github.com/sczhou/CodeFormer",
37
+ """CodeFormer: Towards Robust Blind Face Restoration with Codebook Lookup Transformer (NeurIPS 2022).
38
+ CodeFormer is a Transformer-based model designed to tackle the challenging problem of blind face restoration, where inputs are often severely degraded.
39
+ By framing face restoration as a code prediction task, this approach ensures both improved mapping from degraded inputs to outputs and the generation of visually rich, high-quality faces.
40
+ """],
41
+
42
+ "GPEN-BFR-512.pth" : ["https://huggingface.co/akhaliq/GPEN-BFR-512/resolve/main/GPEN-BFR-512.pth",
43
+ "https://github.com/yangxy/GPEN",
44
+ """GPEN: GAN Prior Embedded Network for Blind Face Restoration in the Wild.
45
+ GPEN addresses blind face restoration (BFR) by embedding a GAN into a U-shaped DNN, combining GAN’s ability to generate high-quality images with DNN’s feature extraction.
46
+ This design reconstructs global structure, fine details, and backgrounds from degraded inputs.
47
+ Simple yet effective, GPEN outperforms state-of-the-art methods, delivering realistic results even for severely degraded images."""],
48
+
49
+ "GPEN-BFR-1024.pt" : ["https://www.modelscope.cn/models/iic/cv_gpen_image-portrait-enhancement-hires/resolve/master/pytorch_model.pt",
50
+ "https://www.modelscope.cn/models/iic/cv_gpen_image-portrait-enhancement-hires/files",
51
+ """The same as GPEN but for 1024 resolution."""],
52
+
53
+ "GPEN-BFR-2048.pt" : ["https://www.modelscope.cn/models/iic/cv_gpen_image-portrait-enhancement-hires/resolve/master/pytorch_model-2048.pt",
54
+ "https://www.modelscope.cn/models/iic/cv_gpen_image-portrait-enhancement-hires/files",
55
+ """The same as GPEN but for 2048 resolution."""],
56
+
57
+ # legacy model
58
+ "GFPGANv1.3.pth" : ["https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.3.pth",
59
+ "https://github.com/TencentARC/GFPGAN/", "The same as GFPGAN but legacy model"],
60
+ "GFPGANv1.2.pth" : ["https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.2.pth",
61
+ "https://github.com/TencentARC/GFPGAN/", "The same as GFPGAN but legacy model"],
62
+ "RestoreFormer.ckpt": ["https://github.com/wzhouxiff/RestoreFormerPlusPlus/releases/download/v1.0.0/RestoreFormer.ckpt",
63
+ "https://github.com/wzhouxiff/RestoreFormerPlusPlus", "The same as RestoreFormer++ but legacy model"],
64
+ }
65
+ upscale_models = {
66
+ # SRVGGNet(Compact)
67
+ "realesr-general-x4v3.pth": ["https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.5.0/realesr-general-x4v3.pth",
68
+ "https://github.com/xinntao/Real-ESRGAN/releases/tag/v0.3.0",
69
+ """Compression Removal, General Upscaler, JPEG, Realistic, Research, Restoration
70
+ xinntao: add realesr-general-x4v3 and realesr-general-wdn-x4v3. They are very tiny models for general scenes, and they may more robust. But as they are tiny models, their performance may be limited."""],
71
+
72
+ "realesr-animevideov3.pth": ["https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.5.0/realesr-animevideov3.pth",
73
+ "https://github.com/xinntao/Real-ESRGAN/releases/tag/v0.2.5.0",
74
+ """Anime, Cartoon, Compression Removal, General Upscaler, JPEG, Realistic, Research, Restoration
75
+ xinntao: update the RealESRGAN AnimeVideo-v3 model, which can achieve better results with a faster inference speed."""],
76
+
77
+ "4xLSDIRCompact.pth": ["https://github.com/Phhofm/models/releases/download/4xLSDIRCompact/4xLSDIRCompact.pth",
78
+ "https://github.com/Phhofm/models/releases/tag/4xLSDIRCompact",
79
+ """Realistic
80
+ Phhofm: Upscale small good quality photos to 4x their size. This is my first ever released self-trained sisr upscaling model."""],
81
+
82
+ "4xLSDIRCompactC.pth": ["https://github.com/Phhofm/models/releases/download/4xLSDIRCompactC/4xLSDIRCompactC.pth",
83
+ "https://github.com/Phhofm/models/releases/tag/4xLSDIRCompactC",
84
+ """Compression Removal, JPEG, Realistic, Restoration
85
+ Phhofm: 4x photo upscaler that handler jpg compression. Trying to extend my previous model to be able to handle compression (JPG 100-30) by manually altering the training dataset, since 4xLSDIRCompact cant handle compression. Use this instead of 4xLSDIRCompact if your photo has compression (like an image from the web)."""],
86
+
87
+ "4xLSDIRCompactR.pth": ["https://github.com/Phhofm/models/releases/download/4xLSDIRCompactC/4xLSDIRCompactR.pth",
88
+ "https://github.com/Phhofm/models/releases/tag/4xLSDIRCompactC",
89
+ """Compression Removal, Realistic, Restoration
90
+ Phhofm: 4x photo uspcaler that handles jpg compression, noise and slight. Extending my last 4xLSDIRCompact model to Real-ESRGAN, meaning trained on synthetic data instead to handle more kinds of degradations, it should be able to handle compression, noise, and slight blur."""],
91
+
92
+ "4xLSDIRCompactN.pth": ["https://github.com/Phhofm/models/releases/download/4xLSDIRCompact3/4xLSDIRCompactC3.pth",
93
+ "https://github.com/Phhofm/models/releases/tag/4xLSDIRCompact3",
94
+ """Realistic
95
+ Phhofm: Upscale good quality input photos to x4 their size. The original 4xLSDIRCompact a bit more trained, cannot handle degradation.
96
+ I am releasing the Series 3 from my 4xLSDIRCompact models. In general my suggestion is, if you have good quality input images use 4xLSDIRCompactN3, otherwise try 4xLSDIRCompactC3 which will be able to handle jpg compression and a bit of blur, or then 4xLSDIRCompactCR3, which is an interpolation between C3 and R3 to be able to handle a bit of noise additionally."""],
97
+
98
+ "4xLSDIRCompactC3.pth": ["https://github.com/Phhofm/models/releases/download/4xLSDIRCompact3/4xLSDIRCompactC3.pth",
99
+ "https://github.com/Phhofm/models/releases/tag/4xLSDIRCompact3",
100
+ """Compression Removal,
101
+ JPEG, Realistic, Restoration
102
+ Phhofm: Upscale compressed photos to x4 their size. Able to handle JPG compression (30-100).
103
+ I am releasing the Series 3 from my 4xLSDIRCompact models. In general my suggestion is, if you have good quality input images use 4xLSDIRCompactN3, otherwise try 4xLSDIRCompactC3 which will be able to handle jpg compression and a bit of blur, or then 4xLSDIRCompactCR3, which is an interpolation between C3 and R3 to be able to handle a bit of noise additionally."""],
104
+
105
+ "4xLSDIRCompactR3.pth": ["https://github.com/Phhofm/models/releases/download/4xLSDIRCompact3/4xLSDIRCompactR3.pth",
106
+ "https://github.com/Phhofm/models/releases/tag/4xLSDIRCompact3",
107
+ """Realistic, Restoration
108
+ Phhofm: Upscale (degraded) photos to x4 their size. Trained on synthetic data, meant to handle more degradations.
109
+ I am releasing the Series 3 from my 4xLSDIRCompact models. In general my suggestion is, if you have good quality input images use 4xLSDIRCompactN3, otherwise try 4xLSDIRCompactC3 which will be able to handle jpg compression and a bit of blur, or then 4xLSDIRCompactCR3, which is an interpolation between C3 and R3 to be able to handle a bit of noise additionally."""],
110
+
111
+ "4xLSDIRCompactCR3.pth": ["https://github.com/Phhofm/models/releases/download/4xLSDIRCompact3/4xLSDIRCompactCR3.pth",
112
+ "https://github.com/Phhofm/models/releases/tag/4xLSDIRCompact3",
113
+ """Phhofm: I am releasing the Series 3 from my 4xLSDIRCompact models. In general my suggestion is, if you have good quality input images use 4xLSDIRCompactN3, otherwise try 4xLSDIRCompactC3 which will be able to handle jpg compression and a bit of blur, or then 4xLSDIRCompactCR3, which is an interpolation between C3 and R3 to be able to handle a bit of noise additionally."""],
114
+
115
+ "2xParimgCompact.pth": ["https://github.com/Phhofm/models/releases/download/2xParimgCompact/2xParimgCompact.pth",
116
+ "https://github.com/Phhofm/models/releases/tag/2xParimgCompact",
117
+ """Realistic
118
+ Phhofm: A 2x photo upscaling compact model based on Microsoft's ImagePairs. This was one of the earliest models I started training and finished it now for release. As can be seen in the examples, this model will affect colors."""],
119
+
120
+ "1xExposureCorrection_compact.pth": ["https://github.com/Phhofm/models/releases/download/1xExposureCorrection_compact/1xExposureCorrection_compact.pth",
121
+ "https://github.com/Phhofm/models/releases/tag/1xExposureCorrection_compact",
122
+ """Restoration
123
+ Phhofm: This model is meant as an experiment to see if compact can be used to train on photos to exposure correct those using the pixel, perceptual, color, color and ldl losses. There is no brightness loss. Still it seems to kinda work."""],
124
+
125
+ "1xUnderExposureCorrection_compact.pth": ["https://github.com/Phhofm/models/releases/download/1xExposureCorrection_compact/1xUnderExposureCorrection_compact.pth",
126
+ "https://github.com/Phhofm/models/releases/tag/1xExposureCorrection_compact",
127
+ """Restoration
128
+ Phhofm: This model is meant as an experiment to see if compact can be used to train on underexposed images to exposure correct those using the pixel, perceptual, color, color and ldl losses. There is no brightness loss. Still it seems to kinda work."""],
129
+
130
+ "1xOverExposureCorrection_compact.pth": ["https://github.com/Phhofm/models/releases/download/1xExposureCorrection_compact/1xOverExposureCorrection_compact.pth",
131
+ "https://github.com/Phhofm/models/releases/tag/1xExposureCorrection_compact",
132
+ """Restoration
133
+ Phhofm: This model is meant as an experiment to see if compact can be used to train on overexposed images to exposure correct those using the pixel, perceptual, color, color and ldl losses. There is no brightness loss. Still it seems to kinda work."""],
134
+
135
+ "2x-sudo-UltraCompact.pth": ["https://objectstorage.us-phoenix-1.oraclecloud.com/n/ax6ygfvpvzka/b/open-modeldb-files/o/2x-sudo-UltraCompact.pth",
136
+ "https://openmodeldb.info/models/2x-sudo-UltraCompact",
137
+ """Anime, Cartoon, Restoration
138
+ sudo: Realtime animation restauration and doing stuff like deblur and compression artefact removal.
139
+ My first attempt to make a REALTIME 2x upscaling model while also applying teacher student learning.
140
+ (Teacher: RealESRGANv2-animevideo-xsx2.pth)"""],
141
+
142
+ "2x_AnimeJaNai_HD_V3_SuperUltraCompact.pth": ["https://github.com/the-database/mpv-upscale-2x_animejanai/releases/download/3.0.0/2x_AnimeJaNai_HD_V3_ModelsOnly.zip",
143
+ "https://openmodeldb.info/models/2x-AnimeJaNai-HD-V3-SuperUltraCompact",
144
+ """Anime, Compression Removal, Restoration
145
+ the-database: Real-time 2x Real-ESRGAN Compact/UltraCompact/SuperUltraCompact models designed for upscaling 1080p anime to 4K.
146
+ The aim of these models is to address scaling, blur, oversharpening, and compression artifacts while upscaling to deliver a result that appears as if the anime was originally mastered in 4K resolution."""],
147
+
148
+ "2x_AnimeJaNai_HD_V3_UltraCompact.pth": ["https://github.com/the-database/mpv-upscale-2x_animejanai/releases/download/3.0.0/2x_AnimeJaNai_HD_V3_ModelsOnly.zip",
149
+ "https://openmodeldb.info/models/2x-AnimeJaNai-HD-V3-UltraCompact",
150
+ """Anime, Compression Removal, Restoration
151
+ the-database: Real-time 2x Real-ESRGAN Compact/UltraCompact/SuperUltraCompact models designed for upscaling 1080p anime to 4K.
152
+ The aim of these models is to address scaling, blur, oversharpening, and compression artifacts while upscaling to deliver a result that appears as if the anime was originally mastered in 4K resolution."""],
153
+
154
+ "2x_AnimeJaNai_HD_V3_Compact.pth": ["https://github.com/the-database/mpv-upscale-2x_animejanai/releases/download/3.0.0/2x_AnimeJaNai_HD_V3_ModelsOnly.zip",
155
+ "https://openmodeldb.info/models/2x-AnimeJaNai-HD-V3-Compact",
156
+ """Anime, Compression Removal, Restoration
157
+ the-database: Real-time 2x Real-ESRGAN Compact/UltraCompact/SuperUltraCompact models designed for upscaling 1080p anime to 4K.
158
+ The aim of these models is to address scaling, blur, oversharpening, and compression artifacts while upscaling to deliver a result that appears as if the anime was originally mastered in 4K resolution."""],
159
+
160
+ # RRDBNet
161
+ "RealESRGAN_x4plus_anime_6B.pth": ["https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.2.4/RealESRGAN_x4plus_anime_6B.pth",
162
+ "https://github.com/xinntao/Real-ESRGAN/releases/tag/v0.2.2.4",
163
+ """Anime, Cartoon, Compression Removal, General Upscaler, JPEG, Realistic, Research, Restoration
164
+ xinntao: We add RealESRGAN_x4plus_anime_6B.pth, which is optimized for anime images with much smaller model size. More details and comparisons with waifu2x are in anime_model.md"""],
165
+
166
+ "RealESRGAN_x2plus.pth" : ["https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.1/RealESRGAN_x2plus.pth",
167
+ "https://github.com/xinntao/Real-ESRGAN/releases/tag/v0.2.1",
168
+ """Compression Removal, General Upscaler, JPEG, Realistic, Research, Restoration
169
+ xinntao: Add RealESRGAN_x2plus.pth model"""],
170
+
171
+ "RealESRNet_x4plus.pth" : ["https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.1/RealESRNet_x4plus.pth",
172
+ "https://github.com/xinntao/Real-ESRGAN/releases/tag/v0.1.1",
173
+ """Compression Removal, General Upscaler, JPEG, Realistic, Research, Restoration
174
+ xinntao: This release is mainly for storing pre-trained models and executable files."""],
175
+
176
+ "RealESRGAN_x4plus.pth" : ["https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth",
177
+ "https://github.com/xinntao/Real-ESRGAN/releases/tag/v0.1.0",
178
+ """Compression Removal, General Upscaler, JPEG, Realistic, Research, Restoration
179
+ xinntao: This release is mainly for storing pre-trained models and executable files."""],
180
+
181
+ # ESRGAN(oldRRDB)
182
+ "4x-AnimeSharp.pth": ["https://huggingface.co/utnah/esrgan/resolve/main/4x-AnimeSharp.pth?download=true",
183
+ "https://openmodeldb.info/models/4x-AnimeSharp",
184
+ """Anime, Cartoon, Text
185
+ Kim2091: Interpolation between 4x-UltraSharp and 4x-TextSharp-v0.5. Works amazingly on anime. It also upscales text, but it's far better with anime content."""],
186
+
187
+ "4x_IllustrationJaNai_V1_ESRGAN_135k.pth": ["https://drive.google.com/uc?export=download&confirm=1&id=1qpioSqBkB_IkSBhEAewSSNFt6qgkBimP",
188
+ "https://openmodeldb.info/models/4x-IllustrationJaNai-V1-DAT2",
189
+ """Anime, Cartoon, Compression Removal, Dehalftone, General Upscaler, JPEG, Manga, Restoration
190
+ the-database: Model for color images including manga covers and color illustrations, digital art, visual novel art, artbooks, and more.
191
+ DAT2 version is the highest quality version but also the slowest. See the ESRGAN version for faster performance."""],
192
+
193
+ "2x-sudo-RealESRGAN.pth": ["https://objectstorage.us-phoenix-1.oraclecloud.com/n/ax6ygfvpvzka/b/open-modeldb-files/o/2x-sudo-RealESRGAN.pth",
194
+ "https://openmodeldb.info/models/2x-sudo-RealESRGAN",
195
+ """Anime, Cartoon
196
+ sudo: Tried to make the best 2x model there is for drawings. I think i archived that.
197
+ And yes, it is nearly 3.8 million iterations (probably a record nobody will beat here), took me nearly half a year to train.
198
+ It can happen that in one edge is a noisy pattern in edges. You can use padding/crop for that.
199
+ I aimed for perceptual quality without zooming in like 400%. Since RealESRGAN is 4x, I downscaled these images with bicubic.
200
+ Pretrained: Pretrained_Model_G: RealESRGAN_x4plus_anime_6B.pth / RealESRGAN_x4plus_anime_6B.pth (sudo_RealESRGAN2x_3.332.758_G.pth)"""],
201
+
202
+ "2x-sudo-RealESRGAN-Dropout.pth": ["https://objectstorage.us-phoenix-1.oraclecloud.com/n/ax6ygfvpvzka/b/open-modeldb-files/o/2x-sudo-RealESRGAN-Dropout.pth",
203
+ "https://openmodeldb.info/models/2x-sudo-RealESRGAN-Dropout",
204
+ """Anime, Cartoon
205
+ sudo: Tried to make the best 2x model there is for drawings. I think i archived that.
206
+ And yes, it is nearly 3.8 million iterations (probably a record nobody will beat here), took me nearly half a year to train.
207
+ It can happen that in one edge is a noisy pattern in edges. You can use padding/crop for that.
208
+ I aimed for perceptual quality without zooming in like 400%. Since RealESRGAN is 4x, I downscaled these images with bicubic.
209
+ Pretrained: Pretrained_Model_G: RealESRGAN_x4plus_anime_6B.pth / RealESRGAN_x4plus_anime_6B.pth (sudo_RealESRGAN2x_3.332.758_G.pth)"""],
210
+
211
+ "4xNomos2_otf_esrgan.pth": ["https://github.com/Phhofm/models/releases/download/4xNomos2_otf_esrgan/4xNomos2_otf_esrgan.pth",
212
+ "https://github.com/Phhofm/models/releases/tag/4xNomos2_otf_esrgan",
213
+ """Compression Removal, JPEG, Realistic, Restoration
214
+ Phhofm: Restoration, 4x ESRGAN model for photography, trained using the Real-ESRGAN otf degradation pipeline."""],
215
+
216
+ "4xNomosWebPhoto_esrgan.pth": ["https://github.com/Phhofm/models/releases/download/4xNomosWebPhoto_esrgan/4xNomosWebPhoto_esrgan.pth",
217
+ "https://github.com/Phhofm/models/releases/tag/4xNomosWebPhoto_esrgan",
218
+ """Realistic, Restoration
219
+ Phhofm: Restoration, 4x ESRGAN model for photography, trained with realistic noise, lens blur, jpg and webp re-compression.
220
+ ESRGAN version of 4xNomosWebPhoto_RealPLKSR, trained on the same dataset and in the same way."""],
221
+
222
+
223
+ "4x_foolhardy_Remacri.pth": ["https://civitai.com/api/download/models/164821?type=Model&format=PickleTensor",
224
+ "https://openmodeldb.info/models/4x-Remacri",
225
+ """Original
226
+ FoolhardyVEVO: A creation of BSRGAN with more details and less smoothing, made by interpolating IRL models such as Siax,
227
+ Superscale, Superscale Artisoft, Pixel Perfect, etc. This was, things like skin and other details don't become mushy and blurry."""],
228
+
229
+ "4x_foolhardy_Remacri_ExtraSmoother.pth": ["https://civitai.com/api/download/models/164822?type=Model&format=PickleTensor",
230
+ "https://openmodeldb.info/models/4x-Remacri",
231
+ """ExtraSmoother
232
+ FoolhardyVEVO: A creation of BSRGAN with more details and less smoothing, made by interpolating IRL models such as Siax,
233
+ Superscale, Superscale Artisoft, Pixel Perfect, etc. This was, things like skin and other details don't become mushy and blurry."""],
234
+
235
+
236
+ # DATNet
237
+ "4xNomos8kDAT.pth" : ["https://github.com/Phhofm/models/releases/download/4xNomos8kDAT/4xNomos8kDAT.pth",
238
+ "https://openmodeldb.info/models/4x-Nomos8kDAT",
239
+ """Anime, Compression Removal, General Upscaler, JPEG, Realistic, Restoration
240
+ Phhofm: A 4x photo upscaler with otf jpg compression, blur and resize, trained on musl's Nomos8k_sfw dataset for realisic sr, this time based on the DAT arch, as a finetune on the official 4x DAT model."""],
241
+
242
+ "4x-DWTP-DS-dat2-v3.pth" : ["https://objectstorage.us-phoenix-1.oraclecloud.com/n/ax6ygfvpvzka/b/open-modeldb-files/o/4x-DWTP-DS-dat2-v3.pth",
243
+ "https://openmodeldb.info/models/4x-DWTP-DS-dat2-v3",
244
+ """Dehalftone, Restoration
245
+ umzi.x.dead: DAT descreenton model, designed to reduce discrepancies on tiles due to too much loss of the first version, while getting rid of the removal of paper texture"""],
246
+
247
+ "4xBHI_dat2_real.pth" : ["https://github.com/Phhofm/models/releases/download/4xBHI_dat2_real/4xBHI_dat2_real.pth",
248
+ "https://github.com/Phhofm/models/releases/tag/4xBHI_dat2_real",
249
+ """Compression Removal, JPEG, Realistic
250
+ Phhofm: 4x dat2 upscaling model for web and realistic images. It handles realistic noise, some realistic blur, and webp and jpg (re)compression. Trained on my BHI dataset (390'035 training tiles) with degraded LR subset."""],
251
+
252
+ "4xBHI_dat2_otf.pth" : ["https://github.com/Phhofm/models/releases/download/4xBHI_dat2_otf/4xBHI_dat2_otf.pth",
253
+ "https://github.com/Phhofm/models/releases/tag/4xBHI_dat2_otf",
254
+ """Compression Removal, JPEG
255
+ Phhofm: 4x dat2 upscaling model, trained with the real-esrgan otf pipeline on my bhi dataset. Handles noise and compression."""],
256
+
257
+ "4xBHI_dat2_multiblur.pth" : ["https://github.com/Phhofm/models/releases/download/4xBHI_dat2_multiblurjpg/4xBHI_dat2_multiblur.pth",
258
+ "https://github.com/Phhofm/models/releases/tag/4xBHI_dat2_multiblurjpg",
259
+ """Phhofm: the 4xBHI_dat2_multiblur checkpoint (trained to 250000 iters), which cannot handle compression but might give just slightly better output on non-degraded input."""],
260
+
261
+ "4xBHI_dat2_multiblurjpg.pth" : ["https://github.com/Phhofm/models/releases/download/4xBHI_dat2_multiblurjpg/4xBHI_dat2_multiblurjpg.pth",
262
+ "https://github.com/Phhofm/models/releases/tag/4xBHI_dat2_multiblurjpg",
263
+ """Compression Removal, JPEG
264
+ Phhofm: 4x dat2 upscaling model, trained with down_up,linear, cubic_mitchell, lanczos, gauss and box scaling algos, some average, gaussian and anisotropic blurs and jpg compression. Trained on my BHI sisr dataset."""],
265
+
266
+ "4x_IllustrationJaNai_V1_DAT2_190k.pth": ["https://drive.google.com/uc?export=download&confirm=1&id=1qpioSqBkB_IkSBhEAewSSNFt6qgkBimP",
267
+ "https://openmodeldb.info/models/4x-IllustrationJaNai-V1-DAT2",
268
+ """Anime, Cartoon, Compression Removal, Dehalftone, General Upscaler, JPEG, Manga, Restoration
269
+ the-database: Model for color images including manga covers and color illustrations, digital art, visual novel art, artbooks, and more.
270
+ DAT2 version is the highest quality version but also the slowest. See the ESRGAN version for faster performance."""],
271
+
272
+ "4x-PBRify_UpscalerDAT2_V1.pth": ["https://github.com/Kim2091/Kim2091-Models/releases/download/4x-PBRify_UpscalerDAT2_V1/4x-PBRify_UpscalerDAT2_V1.pth",
273
+ "https://github.com/Kim2091/Kim2091-Models/releases/tag/4x-PBRify_UpscalerDAT2_V1",
274
+ """Compression Removal, DDS, Game Textures, Restoration
275
+ Kim2091: Yet another model in the PBRify_Remix series. This is a new upscaler to replace the previous 4x-PBRify_UpscalerSIR-M_V2 model.
276
+ This model far exceeds the quality of the previous, with far more natural detail generation and better reconstruction of lines and edges."""],
277
+
278
+ "4xBHI_dat2_otf_nn.pth": ["https://github.com/Phhofm/models/releases/download/4xBHI_dat2_otf_nn/4xBHI_dat2_otf_nn.pth",
279
+ "https://github.com/Phhofm/models/releases/tag/4xBHI_dat2_otf_nn",
280
+ """Compression Removal, JPEG
281
+ Phhofm: 4x dat2 upscaling model, trained with the real-esrgan otf pipeline but without noise, on my bhi dataset. Handles resizes, and jpg compression."""],
282
+
283
+ # HAT
284
+ "4xNomos8kSCHAT-L.pth" : ["https://github.com/Phhofm/models/releases/download/4xNomos8kSCHAT/4xNomos8kSCHAT-L.pth",
285
+ "https://openmodeldb.info/models/4x-Nomos8kSCHAT-L",
286
+ """Anime, Compression Removal, General Upscaler, JPEG, Realistic, Restoration
287
+ Phhofm: 4x photo upscaler with otf jpg compression and blur, trained on musl's Nomos8k_sfw dataset for realisic sr. Since this is a big model, upscaling might take a while."""],
288
+
289
+ "4xNomos8kSCHAT-S.pth" : ["https://github.com/Phhofm/models/releases/download/4xNomos8kSCHAT/4xNomos8kSCHAT-S.pth",
290
+ "https://openmodeldb.info/models/4x-Nomos8kSCHAT-S",
291
+ """Anime, Compression Removal, General Upscaler, JPEG, Realistic, Restoration
292
+ Phhofm: 4x photo upscaler with otf jpg compression and blur, trained on musl's Nomos8k_sfw dataset for realisic sr. HAT-S version/model."""],
293
+
294
+ "4xNomos8kHAT-L_otf.pth": ["https://github.com/Phhofm/models/releases/download/4xNomos8kHAT-L_otf/4xNomos8kHAT-L_otf.pth",
295
+ "https://openmodeldb.info/models/4x-Nomos8kHAT-L-otf",
296
+ """Faces, General Upscaler, Realistic, Restoration
297
+ Phhofm: 4x photo upscaler trained with otf, handles some jpg compression, some blur and some noise."""],
298
+
299
+ "4xBHI_small_hat-l.pth": ["https://github.com/Phhofm/models/releases/download/4xBHI_small_hat-l/4xBHI_small_hat-l.pth",
300
+ "https://github.com/Phhofm/models/releases/tag/4xBHI_small_hat-l",
301
+ """Phhofm: 4x hat-l upscaling model for good quality input. This model does not handle any degradations.
302
+ This model is rather soft, I tried to balance sharpness and faithfulness/non-artifacts.
303
+ For a bit sharper output, but can generate a bit of artifacts, you can try the 4xBHI_small_hat-l_sharp version,
304
+ also included in this release, which might still feel soft if you are used to sharper outputs."""],
305
+
306
+ # RealPLKSR_dysample
307
+ "4xHFA2k_ludvae_realplksr_dysample.pth": ["https://github.com/Phhofm/models/releases/download/4xHFA2k_ludvae_realplksr_dysample/4xHFA2k_ludvae_realplksr_dysample.pth",
308
+ "https://openmodeldb.info/models/4x-HFA2k-ludvae-realplksr-dysample",
309
+ """Anime, Compression Removal, Restoration
310
+ Phhofm: A Dysample RealPLKSR 4x upscaling model for anime single-image resolution."""],
311
+
312
+ "4xArtFaces_realplksr_dysample.pth" : ["https://github.com/Phhofm/models/releases/download/4xArtFaces_realplksr_dysample/4xArtFaces_realplksr_dysample.pth",
313
+ "https://openmodeldb.info/models/4x-ArtFaces-realplksr-dysample",
314
+ """ArtFaces
315
+ Phhofm: A Dysample RealPLKSR 4x upscaling model for art / painted faces."""],
316
+
317
+ "4x-PBRify_RPLKSRd_V3.pth" : ["https://github.com/Kim2091/Kim2091-Models/releases/download/4x-PBRify_RPLKSRd_V3/4x-PBRify_RPLKSRd_V3.pth",
318
+ "https://github.com/Kim2091/Kim2091-Models/releases/tag/4x-PBRify_RPLKSRd_V3",
319
+ """Compression Removal, DDS, Debanding, Dedither, Dehalo, Game Textures, Restoration
320
+ Kim2091: This update brings a new upscaling model, 4x-PBRify_RPLKSRd_V3. This model is roughly 8x faster than the current DAT2 model, while being higher quality.
321
+ It produces far more natural detail, resolves lines and edges more smoothly, and cleans up compression artifacts better.
322
+ As a result of those improvements, PBR is also much improved. It tends to be clearer with less defined artifacts."""],
323
+
324
+ "4xNomos2_realplksr_dysample.pth" : ["https://github.com/Phhofm/models/releases/download/4xNomos2_realplksr_dysample/4xNomos2_realplksr_dysample.pth",
325
+ "https://openmodeldb.info/models/4x-Nomos2-realplksr-dysample",
326
+ """Compression Removal, JPEG, Realistic, Restoration
327
+ Phhofm: A Dysample RealPLKSR 4x upscaling model that was trained with / handles jpg compression down to 70 on the Nomosv2 dataset, preserves DoF.
328
+ This model affects / saturate colors, which can be counteracted a bit by using wavelet color fix, as used in these examples."""],
329
+
330
+ # RealPLKSR
331
+ "2x-AnimeSharpV2_RPLKSR_Sharp.pth": ["https://github.com/Kim2091/Kim2091-Models/releases/download/2x-AnimeSharpV2_Set/2x-AnimeSharpV2_RPLKSR_Sharp.pth",
332
+ "https://github.com/Kim2091/Kim2091-Models/releases/tag/2x-AnimeSharpV2_Set",
333
+ """Anime, Compression Removal, Restoration
334
+ Kim2091: This is my first anime model in years. Hopefully you guys can find a good use-case for it.
335
+ RealPLKSR (Higher quality, slower) Sharp: For heavily degraded sources. Sharp models have issues depth of field but are best at removing artifacts
336
+ """],
337
+
338
+ "2x-AnimeSharpV2_RPLKSR_Soft.pth" : ["https://github.com/Kim2091/Kim2091-Models/releases/download/2x-AnimeSharpV2_Set/2x-AnimeSharpV2_RPLKSR_Soft.pth",
339
+ "https://github.com/Kim2091/Kim2091-Models/releases/tag/2x-AnimeSharpV2_Set",
340
+ """Anime, Compression Removal, Restoration
341
+ Kim2091: This is my first anime model in years. Hopefully you guys can find a good use-case for it.
342
+ RealPLKSR (Higher quality, slower) Soft: For cleaner sources. Soft models preserve depth of field but may not remove other artifacts as well"""],
343
+
344
+ "4xPurePhoto-RealPLSKR.pth" : ["https://github.com/starinspace/StarinspaceUpscale/releases/download/Models/4xPurePhoto-RealPLSKR.pth",
345
+ "https://openmodeldb.info/models/4x-PurePhoto-RealPLSKR",
346
+ """AI Generated, Compression Removal, JPEG, Realistic, Restoration
347
+ asterixcool: Skilled in working with cats, hair, parties, and creating clear images.
348
+ Also proficient in resizing photos and enlarging large, sharp images.
349
+ Can effectively improve images from small sizes as well (300px at smallest on one side, depending on the subject).
350
+ Experienced in experimenting with techniques like upscaling with this model twice and
351
+ then reducing it by 50% to enhance details, especially in features like hair or animals."""],
352
+
353
+ "2x_Text2HD_v.1-RealPLKSR.pth" : ["https://github.com/starinspace/StarinspaceUpscale/releases/download/Models/2x_Text2HD_v.1-RealPLKSR.pth",
354
+ "https://openmodeldb.info/models/2x-Text2HD-v-1",
355
+ """Compression Removal, Denoise, General Upscaler, JPEG, Restoration, Text
356
+ asterixcool: The upscale model is specifically designed to enhance lower-quality text images,
357
+ improving their clarity and readability by upscaling them by 2x.
358
+ It excels at processing moderately sized text, effectively transforming it into high-quality, legible scans.
359
+ However, the model may encounter challenges when dealing with very small text,
360
+ as its performance is optimized for text of a certain minimum size. For best results,
361
+ input images should contain text that is not excessively small."""],
362
+
363
+ "2xVHS2HD-RealPLKSR.pth" : ["https://github.com/starinspace/StarinspaceUpscale/releases/download/Models/2xVHS2HD-RealPLKSR.pth",
364
+ "https://openmodeldb.info/models/2x-VHS2HD",
365
+ """Compression Removal, Dehalo, Realistic, Restoration, Video Frame
366
+ asterixcool: An advanced VHS recording model designed to enhance video quality by reducing artifacts such as haloing, ghosting, and noise patterns.
367
+ Optimized primarily for PAL resolution (NTSC might work good as well)."""],
368
+
369
+ "4xNomosWebPhoto_RealPLKSR.pth" : ["https://github.com/Phhofm/models/releases/download/4xNomosWebPhoto_RealPLKSR/4xNomosWebPhoto_RealPLKSR.pth",
370
+ "https://openmodeldb.info/models/4x-NomosWebPhoto-RealPLKSR",
371
+ """Realistic, Restoration
372
+ Phhofm: 4x RealPLKSR model for photography, trained with realistic noise, lens blur, jpg and webp re-compression."""],
373
+
374
+ # DRCT
375
+ "4xNomos2_hq_drct-l.pth" : ["https://github.com/Phhofm/models/releases/download/4xNomos2_hq_drct-l/4xNomos2_hq_drct-l.pth",
376
+ "https://github.com/Phhofm/models/releases/tag/4xNomos2_hq_drct-l",
377
+ """General Upscaler, Realistic
378
+ Phhofm: An drct-l 4x upscaling model, similiar to the 4xNomos2_hq_atd, 4xNomos2_hq_dat2 and 4xNomos2_hq_mosr models, trained and for usage on non-degraded input to give good quality output.
379
+ """],
380
+
381
+ # ATD
382
+ "4xNomos2_hq_atd.pth" : ["https://github.com/Phhofm/models/releases/download/4xNomos2_hq_atd/4xNomos2_hq_atd.pth",
383
+ "https://github.com/Phhofm/models/releases/tag/4xNomos2_hq_atd",
384
+ """General Upscaler, Realistic
385
+ Phhofm: An atd 4x upscaling model, similiar to the 4xNomos2_hq_dat2 or 4xNomos2_hq_mosr models, trained and for usage on non-degraded input to give good quality output.
386
+ """],
387
+
388
+ # MoSR
389
+ "4xNomos2_hq_mosr.pth" : ["https://github.com/Phhofm/models/releases/download/4xNomos2_hq_mosr/4xNomos2_hq_mosr.pth",
390
+ "https://github.com/Phhofm/models/releases/tag/4xNomos2_hq_mosr",
391
+ """General Upscaler, Realistic
392
+ Phhofm: A 4x MoSR upscaling model, meant for non-degraded input, since this model was trained on non-degraded input to give good quality output.
393
+ """],
394
+
395
+ "2x-AnimeSharpV2_MoSR_Sharp.pth" : ["https://github.com/Kim2091/Kim2091-Models/releases/download/2x-AnimeSharpV2_Set/2x-AnimeSharpV2_MoSR_Sharp.pth",
396
+ "https://github.com/Kim2091/Kim2091-Models/releases/tag/2x-AnimeSharpV2_Set",
397
+ """Anime, Compression Removal, Restoration
398
+ Kim2091: This is my first anime model in years. Hopefully you guys can find a good use-case for it.
399
+ MoSR (Lower quality, faster), Sharp: For heavily degraded sources. Sharp models have issues depth of field but are best at removing artifacts
400
+ """],
401
+
402
+ "2x-AnimeSharpV2_MoSR_Soft.pth" : ["https://github.com/Kim2091/Kim2091-Models/releases/download/2x-AnimeSharpV2_Set/2x-AnimeSharpV2_MoSR_Soft.pth",
403
+ "https://github.com/Kim2091/Kim2091-Models/releases/tag/2x-AnimeSharpV2_Set",
404
+ """Anime, Compression Removal, Restoration
405
+ Kim2091: This is my first anime model in years. Hopefully you guys can find a good use-case for it.
406
+ MoSR (Lower quality, faster), Soft: For cleaner sources. Soft models preserve depth of field but may not remove other artifacts as well
407
+ """],
408
+
409
+ # SRFormer
410
+ "4xNomos8kSCSRFormer.pth" : ["https://github.com/Phhofm/models/releases/download/4xNomos8kSCSRFormer/4xNomos8kSCSRFormer.pth",
411
+ "https://github.com/Phhofm/models/releases/tag/4xNomos8kSCSRFormer",
412
+ """Anime, Compression Removal, General Upscaler, JPEG, Realistic, Restoration
413
+ Phhofm: 4x photo upscaler with otf jpg compression and blur, trained on musl's Nomos8k_sfw dataset for realisic sr.
414
+ """],
415
+
416
+ "4xFrankendataFullDegradation_SRFormer460K_g.pth" : ["https://drive.google.com/uc?export=download&confirm=1&id=1PZrj-8ofxhORv_OgTVSoRt3dYi-BtiDj",
417
+ "https://openmodeldb.info/models/4x-Frankendata-FullDegradation-SRFormer",
418
+ """Compression Removal, Denoise, Realistic, Restoration
419
+ Crustaceous D: 4x realistic upscaler that may also work for general purpose usage.
420
+ It was trained with OTF random degradation with a very low to very high range of degradations, including blur, noise, and compression.
421
+ Trained with the same Frankendata dataset that I used for the pretrain model.
422
+ """],
423
+
424
+ "4xFrankendataPretrainer_SRFormer400K_g.pth" : ["https://drive.google.com/uc?export=download&confirm=1&id=1SaKvpYYIm2Vj2m9GifUMlNCbmkE6JZmr",
425
+ "https://openmodeldb.info/models/4x-FrankendataPretainer-SRFormer",
426
+ """Realistic, Restoration
427
+ Crustaceous D: 4x realistic upscaler that may also work for general purpose usage.
428
+ It was trained with OTF random degradation with a very low to very high range of degradations, including blur, noise, and compression.
429
+ Trained with the same Frankendata dataset that I used for the pretrain model.
430
+ """],
431
+
432
+ "1xFrankenfixer_SRFormerLight_g.pth" : ["https://drive.google.com/uc?export=download&confirm=1&id=1UJ0iyFn4IGNhPIgNgrQrBxYsdDloFc9I",
433
+ "https://openmodeldb.info/models/1x-Frankenfixer-SRFormerLight",
434
+ """Realistic, Restoration
435
+ Crustaceous D: A 1x model designed to reduce artifacts and restore detail to images upscaled by 4xFrankendata_FullDegradation_SRFormer. It could possibly work with other upscaling models too.
436
+ """],
437
+ }
438
+
439
+ example_list = ["images/a01.jpg", "images/a02.jpg", "images/a03.jpg", "images/a04.jpg", "images/bus.jpg", "images/zidane.jpg",
440
+ "images/b01.jpg", "images/b02.jpg", "images/b03.jpg", "images/b04.jpg", "images/b05.jpg", "images/b06.jpg",
441
+ "images/b07.jpg", "images/b08.jpg", "images/b09.jpg", "images/b10.jpg", "images/b11.jpg", "images/c01.jpg",
442
+ "images/c02.jpg", "images/c03.jpg", "images/c04.jpg", "images/c05.jpg", "images/c06.jpg", "images/c07.jpg",
443
+ "images/c08.jpg", "images/c09.jpg", "images/c10.jpg"]
444
+
445
+ def get_model_type(model_name):
446
+ # Define model type mappings based on key parts of the model names
447
+ model_type = "other"
448
+ if any(value in model_name.lower() for value in ("4x-animesharp.pth", "sudo-realesrgan", "remacri")):
449
+ model_type = "ESRGAN"
450
+ elif "srformer" in model_name.lower():
451
+ model_type = "SRFormer"
452
+ elif ("realplksr" in model_name.lower() and "dysample" in model_name.lower()) or "rplksrd" in model_name.lower():
453
+ model_type = "RealPLKSR_dysample"
454
+ elif any(value in model_name.lower() for value in ("realplksr", "rplksr", "realplskr")):
455
+ model_type = "RealPLKSR"
456
+ elif any(value in model_name.lower() for value in ("realesrgan", "realesrnet")):
457
+ model_type = "RRDB"
458
+ elif any(value in model_name.lower() for value in ("realesr", "compact")):
459
+ model_type = "SRVGG"
460
+ elif "esrgan" in model_name.lower():
461
+ model_type = "ESRGAN"
462
+ elif "dat" in model_name.lower():
463
+ model_type = "DAT"
464
+ elif "hat" in model_name.lower():
465
+ model_type = "HAT"
466
+ elif "drct" in model_name.lower():
467
+ model_type = "DRCT"
468
+ elif "atd" in model_name.lower():
469
+ model_type = "ATD"
470
+ elif "mosr" in model_name.lower():
471
+ model_type = "MoSR"
472
+ return f"{model_type}, {model_name}"
473
+
474
+ typed_upscale_models = {get_model_type(key): value[0] for key, value in upscale_models.items()}
475
+
476
+
477
+ class Upscale:
478
+ def __init__(self,):
479
+ self.scale = 4
480
+ self.modelInUse = ""
481
+ self.realesrganer = None
482
+ self.face_enhancer = None
483
+
484
+ def initBGUpscaleModel(self, upscale_model):
485
+ upscale_type, upscale_model = upscale_model.split(", ", 1)
486
+ download_from_url(upscale_models[upscale_model][0], upscale_model, os.path.join("weights", "upscale"))
487
+ self.modelInUse = f"_{os.path.splitext(upscale_model)[0]}"
488
+ netscale = 1 if any(sub in upscale_model.lower() for sub in ("x1", "1x")) else (2 if any(sub in upscale_model.lower() for sub in ("x2", "2x")) else 4)
489
+ model = None
490
+ half = True if torch.cuda.is_available() else False
491
+ if upscale_type:
492
+ # The values of the following hyperparameters are based on the research findings of the Spandrel project.
493
+ # https://github.com/chaiNNer-org/spandrel/tree/main/libs/spandrel/spandrel/architectures
494
+ from basicsr.archs.rrdbnet_arch import RRDBNet
495
+ loadnet = torch.load(os.path.join("weights", "upscale", upscale_model), map_location=torch.device('cpu'), weights_only=True)
496
+ if 'params_ema' in loadnet or 'params' in loadnet:
497
+ loadnet = loadnet['params_ema'] if 'params_ema' in loadnet else loadnet['params']
498
+
499
+ if upscale_type == "SRVGG":
500
+ from basicsr.archs.srvgg_arch import SRVGGNetCompact
501
+ body_max_num = self.find_max_numbers(loadnet, "body")
502
+ num_feat = loadnet["body.0.weight"].shape[0]
503
+ num_in_ch = loadnet["body.0.weight"].shape[1]
504
+ num_conv = body_max_num // 2 - 1
505
+ model = SRVGGNetCompact(num_in_ch=num_in_ch, num_out_ch=3, num_feat=num_feat, num_conv=num_conv, upscale=netscale, act_type='prelu')
506
+ elif upscale_type == "RRDB" or upscale_type == "ESRGAN":
507
+ if upscale_type == "RRDB":
508
+ num_block = self.find_max_numbers(loadnet, "body") + 1
509
+ num_feat = loadnet["conv_first.weight"].shape[0]
510
+ else:
511
+ num_block = self.find_max_numbers(loadnet, "model.1.sub")
512
+ num_feat = loadnet["model.0.weight"].shape[0]
513
+ model = RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=num_feat, num_block=num_block, num_grow_ch=32, scale=netscale, is_real_esrgan=upscale_type == "RRDB")
514
+ elif upscale_type == "DAT":
515
+ from basicsr.archs.dat_arch import DAT
516
+ half = False
517
+
518
+ in_chans = loadnet["conv_first.weight"].shape[1]
519
+ embed_dim = loadnet["conv_first.weight"].shape[0]
520
+ num_layers = self.find_max_numbers(loadnet, "layers") + 1
521
+ depth = [6] * num_layers
522
+ num_heads = [6] * num_layers
523
+ for i in range(num_layers):
524
+ depth[i] = self.find_max_numbers(loadnet, f"layers.{i}.blocks") + 1
525
+ num_heads[i] = loadnet[f"layers.{i}.blocks.1.attn.temperature"].shape[0] if depth[i] >= 2 else \
526
+ loadnet[f"layers.{i}.blocks.0.attn.attns.0.pos.pos3.2.weight"].shape[0] * 2
527
+
528
+ upsampler = "pixelshuffle" if "conv_last.weight" in loadnet else "pixelshuffledirect"
529
+ resi_connection = "1conv" if "conv_after_body.weight" in loadnet else "3conv"
530
+ qkv_bias = "layers.0.blocks.0.attn.qkv.bias" in loadnet
531
+ expansion_factor = float(loadnet["layers.0.blocks.0.ffn.fc1.weight"].shape[0] / embed_dim)
532
+
533
+ img_size = 64
534
+ if "layers.0.blocks.2.attn.attn_mask_0" in loadnet:
535
+ attn_mask_0_x, attn_mask_0_y, _attn_mask_0_z = loadnet["layers.0.blocks.2.attn.attn_mask_0"].shape
536
+ img_size = int(math.sqrt(attn_mask_0_x * attn_mask_0_y))
537
+
538
+ split_size = [2, 4]
539
+ if "layers.0.blocks.0.attn.attns.0.rpe_biases" in loadnet:
540
+ split_sizes = loadnet["layers.0.blocks.0.attn.attns.0.rpe_biases"][-1] + 1
541
+ split_size = [int(x) for x in split_sizes]
542
+
543
+ model = DAT(img_size=img_size, in_chans=in_chans, embed_dim=embed_dim, split_size=split_size, depth=depth, num_heads=num_heads, expansion_factor=expansion_factor,
544
+ qkv_bias=qkv_bias, resi_connection=resi_connection, upsampler=upsampler, upscale=netscale)
545
+ elif upscale_type == "HAT":
546
+ half = False
547
+ from basicsr.archs.hat_arch import HAT
548
+ in_chans = loadnet["conv_first.weight"].shape[1]
549
+ embed_dim = loadnet["conv_first.weight"].shape[0]
550
+ window_size = int(math.sqrt(loadnet["relative_position_index_SA"].shape[0]))
551
+ num_layers = self.find_max_numbers(loadnet, "layers") + 1
552
+ depths = [6] * num_layers
553
+ num_heads = [6] * num_layers
554
+ for i in range(num_layers):
555
+ depths[i] = self.find_max_numbers(loadnet, f"layers.{i}.residual_group.blocks") + 1
556
+ num_heads[i] = loadnet[f"layers.{i}.residual_group.overlap_attn.relative_position_bias_table"].shape[1]
557
+ resi_connection = "1conv" if "conv_after_body.weight" in loadnet else "identity"
558
+
559
+ compress_ratio = self.find_divisor_for_quotient(embed_dim, loadnet["layers.0.residual_group.blocks.0.conv_block.cab.0.weight"].shape[0],)
560
+ squeeze_factor = self.find_divisor_for_quotient(embed_dim, loadnet["layers.0.residual_group.blocks.0.conv_block.cab.3.attention.1.weight"].shape[0],)
561
+
562
+ qkv_bias = "layers.0.residual_group.blocks.0.attn.qkv.bias" in loadnet
563
+ patch_norm = "patch_embed.norm.weight" in loadnet
564
+ ape = "absolute_pos_embed" in loadnet
565
+
566
+ mlp_hidden_dim = int(loadnet["layers.0.residual_group.blocks.0.mlp.fc1.weight"].shape[0])
567
+ mlp_ratio = mlp_hidden_dim / embed_dim
568
+ upsampler = "pixelshuffle"
569
+
570
+ model = HAT(img_size=64, patch_size=1, in_chans=in_chans, embed_dim=embed_dim, depths=depths, num_heads=num_heads, window_size=window_size, compress_ratio=compress_ratio,
571
+ squeeze_factor=squeeze_factor, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, ape=ape, patch_norm=patch_norm,
572
+ upsampler=upsampler, resi_connection=resi_connection, upscale=netscale,)
573
+ elif "RealPLKSR" in upscale_type:
574
+ from basicsr.archs.realplksr_arch import realplksr
575
+ half = False if "RealPLSKR" in upscale_model else half
576
+ use_ea = "feats.1.attn.f.0.weight" in loadnet
577
+ dim = loadnet["feats.0.weight"].shape[0]
578
+ num_feats = self.find_max_numbers(loadnet, "feats") + 1
579
+ n_blocks = num_feats - 3
580
+ kernel_size = loadnet["feats.1.lk.conv.weight"].shape[2]
581
+ split_ratio = loadnet["feats.1.lk.conv.weight"].shape[0] / dim
582
+ use_dysample = "to_img.init_pos" in loadnet
583
+
584
+ model = realplksr(upscaling_factor=netscale, dim=dim, n_blocks=n_blocks, kernel_size=kernel_size, split_ratio=split_ratio, use_ea=use_ea, dysample=use_dysample)
585
+ elif upscale_type == "DRCT":
586
+ half = False
587
+ from basicsr.archs.DRCT_arch import DRCT
588
+
589
+ in_chans = loadnet["conv_first.weight"].shape[1]
590
+ embed_dim = loadnet["conv_first.weight"].shape[0]
591
+ num_layers = self.find_max_numbers(loadnet, "layers") + 1
592
+ depths = (6,) * num_layers
593
+ num_heads = []
594
+ for i in range(num_layers):
595
+ num_heads.append(loadnet[f"layers.{i}.swin1.attn.relative_position_bias_table"].shape[1])
596
+
597
+ mlp_ratio = loadnet["layers.0.swin1.mlp.fc1.weight"].shape[0] / embed_dim
598
+ window_square = loadnet["layers.0.swin1.attn.relative_position_bias_table"].shape[0]
599
+ window_size = (math.isqrt(window_square) + 1) // 2
600
+ upsampler = "pixelshuffle" if "conv_last.weight" in loadnet else ""
601
+ resi_connection = "1conv" if "conv_after_body.weight" in loadnet else ""
602
+ qkv_bias = "layers.0.swin1.attn.qkv.bias" in loadnet
603
+ gc_adjust1 = loadnet["layers.0.adjust1.weight"].shape[0]
604
+ patch_norm = "patch_embed.norm.weight" in loadnet
605
+ ape = "absolute_pos_embed" in loadnet
606
+
607
+ model = DRCT(in_chans=in_chans, img_size= 64, window_size=window_size, compress_ratio=3,squeeze_factor=30,
608
+ conv_scale= 0.01, overlap_ratio= 0.5, img_range= 1., depths=depths, embed_dim=embed_dim, num_heads=num_heads,
609
+ mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, ape=ape, patch_norm=patch_norm, use_checkpoint=False,
610
+ upscale=netscale, upsampler=upsampler, resi_connection=resi_connection, gc =gc_adjust1,)
611
+ elif upscale_type == "ATD":
612
+ half = False
613
+ from basicsr.archs.atd_arch import ATD
614
+ in_chans = loadnet["conv_first.weight"].shape[1]
615
+ embed_dim = loadnet["conv_first.weight"].shape[0]
616
+ window_size = math.isqrt(loadnet["relative_position_index_SA"].shape[0])
617
+ num_layers = self.find_max_numbers(loadnet, "layers") + 1
618
+ depths = [6] * num_layers
619
+ num_heads = [6] * num_layers
620
+ for i in range(num_layers):
621
+ depths[i] = self.find_max_numbers(loadnet, f"layers.{i}.residual_group.layers") + 1
622
+ num_heads[i] = loadnet[f"layers.{i}.residual_group.layers.0.attn_win.relative_position_bias_table"].shape[1]
623
+ num_tokens = loadnet["layers.0.residual_group.layers.0.attn_atd.scale"].shape[0]
624
+ reducted_dim = loadnet["layers.0.residual_group.layers.0.attn_atd.wq.weight"].shape[0]
625
+ convffn_kernel_size = loadnet["layers.0.residual_group.layers.0.convffn.dwconv.depthwise_conv.0.weight"].shape[2]
626
+ mlp_ratio = (loadnet["layers.0.residual_group.layers.0.convffn.fc1.weight"].shape[0] / embed_dim)
627
+ qkv_bias = "layers.0.residual_group.layers.0.wqkv.bias" in loadnet
628
+ ape = "absolute_pos_embed" in loadnet
629
+ patch_norm = "patch_embed.norm.weight" in loadnet
630
+ resi_connection = "1conv" if "layers.0.conv.weight" in loadnet else "3conv"
631
+
632
+ if "conv_up1.weight" in loadnet:
633
+ upsampler = "nearest+conv"
634
+ elif "conv_before_upsample.0.weight" in loadnet:
635
+ upsampler = "pixelshuffle"
636
+ elif "conv_last.weight" in loadnet:
637
+ upsampler = ""
638
+ else:
639
+ upsampler = "pixelshuffledirect"
640
+
641
+ is_light = upsampler == "pixelshuffledirect" and embed_dim == 48
642
+ category_size = 128 if is_light else 256
643
+
644
+ model = ATD(in_chans=in_chans, embed_dim=embed_dim, depths=depths, num_heads=num_heads, window_size=window_size, category_size=category_size,
645
+ num_tokens=num_tokens, reducted_dim=reducted_dim, convffn_kernel_size=convffn_kernel_size, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, ape=ape,
646
+ patch_norm=patch_norm, use_checkpoint=False, upscale=netscale, upsampler=upsampler, resi_connection='1conv',)
647
+ elif upscale_type == "MoSR":
648
+ from basicsr.archs.mosr_arch import mosr
649
+ n_block = self.find_max_numbers(loadnet, "gblocks") - 5
650
+ in_ch = loadnet["gblocks.0.weight"].shape[1]
651
+ out_ch = loadnet["upsampler.end_conv.weight"].shape[0] if "upsampler.init_pos" in loadnet else in_ch
652
+ dim = loadnet["gblocks.0.weight"].shape[0]
653
+ expansion_ratio = (loadnet["gblocks.1.fc1.weight"].shape[0] / loadnet["gblocks.1.fc1.weight"].shape[1]) / 2
654
+ conv_ratio = loadnet["gblocks.1.conv.weight"].shape[0] / dim
655
+ kernel_size = loadnet["gblocks.1.conv.weight"].shape[2]
656
+ upsampler = "dys" if "upsampler.init_pos" in loadnet else ("gps" if "upsampler.in_to_k.weight" in loadnet else "ps")
657
+
658
+ model = mosr(in_ch = in_ch, out_ch = out_ch, upscale = netscale, n_block = n_block, dim = dim,
659
+ upsampler = upsampler, kernel_size = kernel_size, expansion_ratio = expansion_ratio, conv_ratio = conv_ratio,)
660
+ elif upscale_type == "SRFormer":
661
+ half = False
662
+ from basicsr.archs.srformer_arch import SRFormer
663
+ in_chans = loadnet["conv_first.weight"].shape[1]
664
+ embed_dim = loadnet["conv_first.weight"].shape[0]
665
+ ape = "absolute_pos_embed" in loadnet
666
+ patch_norm = "patch_embed.norm.weight" in loadnet
667
+ qkv_bias = "layers.0.residual_group.blocks.0.attn.q.bias" in loadnet
668
+ mlp_ratio = float(loadnet["layers.0.residual_group.blocks.0.mlp.fc1.weight"].shape[0] / embed_dim)
669
+
670
+ num_layers = self.find_max_numbers(loadnet, "layers") + 1
671
+ depths = [6] * num_layers
672
+ num_heads = [6] * num_layers
673
+ for i in range(num_layers):
674
+ depths[i] = self.find_max_numbers(loadnet, f"layers.{i}.residual_group.blocks") + 1
675
+ num_heads[i] = loadnet[f"layers.{i}.residual_group.blocks.0.attn.relative_position_bias_table"].shape[1]
676
+
677
+ if "conv_hr.weight" in loadnet:
678
+ upsampler = "nearest+conv"
679
+ elif "conv_before_upsample.0.weight" in loadnet:
680
+ upsampler = "pixelshuffle"
681
+ elif "upsample.0.weight" in loadnet:
682
+ upsampler = "pixelshuffledirect"
683
+ resi_connection = "1conv" if "conv_after_body.weight" in loadnet else "3conv"
684
+
685
+ window_size = int(math.sqrt(loadnet["layers.0.residual_group.blocks.0.attn.relative_position_bias_table"].shape[0])) + 1
686
+
687
+ if "layers.0.residual_group.blocks.1.attn_mask" in loadnet:
688
+ attn_mask_0 = loadnet["layers.0.residual_group.blocks.1.attn_mask"].shape[0]
689
+ patches_resolution = int(math.sqrt(attn_mask_0) * window_size)
690
+ else:
691
+ patches_resolution = window_size
692
+ if ape:
693
+ pos_embed_value = loadnet.get("absolute_pos_embed", [None, None])[1]
694
+ if pos_embed_value:
695
+ patches_resolution = int(math.sqrt(pos_embed_value))
696
+
697
+ img_size = patches_resolution
698
+ if img_size % window_size != 0:
699
+ for nice_number in [512, 256, 128, 96, 64, 48, 32, 24, 16]:
700
+ if nice_number % window_size != 0:
701
+ nice_number += window_size - (nice_number % window_size)
702
+ if nice_number == patches_resolution:
703
+ img_size = nice_number
704
+ break
705
+
706
+ model = SRFormer(img_size=img_size, in_chans=in_chans, embed_dim=embed_dim, depths=depths, num_heads=num_heads, window_size=window_size, mlp_ratio=mlp_ratio,
707
+ qkv_bias=qkv_bias, qk_scale=None, ape=ape, patch_norm=patch_norm, upscale=netscale, upsampler=upsampler, resi_connection=resi_connection,)
708
+
709
+ if model:
710
+ self.realesrganer = RealESRGANer(scale=netscale, model_path=os.path.join("weights", "upscale", upscale_model), model=model, tile=0, tile_pad=10, pre_pad=0, half=half)
711
+ elif upscale_model:
712
+ import PIL
713
+ from image_gen_aux import UpscaleWithModel
714
+ class UpscaleWithModel_Gfpgan(UpscaleWithModel):
715
+ def cv2pil(self, image):
716
+ ''' OpenCV type -> PIL type
717
+ https://qiita.com/derodero24/items/f22c22b22451609908ee
718
+ '''
719
+ new_image = image.copy()
720
+ if new_image.ndim == 2: # Grayscale
721
+ pass
722
+ elif new_image.shape[2] == 3: # Color
723
+ new_image = cv2.cvtColor(new_image, cv2.COLOR_BGR2RGB)
724
+ elif new_image.shape[2] == 4: # Transparency
725
+ new_image = cv2.cvtColor(new_image, cv2.COLOR_BGRA2RGBA)
726
+ new_image = PIL.Image.fromarray(new_image)
727
+ return new_image
728
+
729
+ def pil2cv(self, image):
730
+ ''' PIL type -> OpenCV type
731
+ https://qiita.com/derodero24/items/f22c22b22451609908ee
732
+ '''
733
+ new_image = np.array(image, dtype=np.uint8)
734
+ if new_image.ndim == 2: # Grayscale
735
+ pass
736
+ elif new_image.shape[2] == 3: # Color
737
+ new_image = cv2.cvtColor(new_image, cv2.COLOR_RGB2BGR)
738
+ elif new_image.shape[2] == 4: # Transparency
739
+ new_image = cv2.cvtColor(new_image, cv2.COLOR_RGBA2BGRA)
740
+ return new_image
741
+
742
+ def enhance(self, img, outscale=None):
743
+ # img: numpy array
744
+ h_input, w_input = img.shape[0:2]
745
+ pil_img = self.cv2pil(img)
746
+ pil_img = self.__call__(pil_img)
747
+ cv_image = self.pil2cv(pil_img)
748
+ if outscale is not None and outscale != float(netscale):
749
+ interpolation = cv2.INTER_AREA if outscale < float(netscale) else cv2.INTER_LANCZOS4
750
+ cv_image = cv2.resize(
751
+ cv_image, (
752
+ int(w_input * outscale),
753
+ int(h_input * outscale),
754
+ ), interpolation=interpolation)
755
+ return cv_image, None
756
+
757
+ device = "cuda" if torch.cuda.is_available() else "cpu"
758
+ upscaler = UpscaleWithModel.from_pretrained(os.path.join("weights", "upscale", upscale_model)).to(device)
759
+ upscaler.__class__ = UpscaleWithModel_Gfpgan
760
+ self.realesrganer = upscaler
761
+
762
+
763
+ def initFaceEnhancerModel(self, face_restoration, face_detection):
764
+ model_rootpath = os.path.join("weights", "face")
765
+ model_path = os.path.join(model_rootpath, face_restoration)
766
+ download_from_url(face_models[face_restoration][0], face_restoration, model_rootpath)
767
+
768
+ self.modelInUse = f"_{os.path.splitext(face_restoration)[0]}" + self.modelInUse
769
+ from gfpgan.utils import GFPGANer
770
+ resolution = 512
771
+ channel_multiplier = None
772
+
773
+ if face_restoration and face_restoration.startswith("GFPGANv1."):
774
+ arch = "clean"
775
+ channel_multiplier = 2
776
+ elif face_restoration and face_restoration.startswith("RestoreFormer"):
777
+ arch = "RestoreFormer++" if face_restoration.startswith("RestoreFormer++") else "RestoreFormer"
778
+ elif face_restoration == 'CodeFormer.pth':
779
+ arch = "CodeFormer"
780
+ elif face_restoration.startswith("GPEN-BFR-"):
781
+ arch = "GPEN"
782
+ channel_multiplier = 2
783
+ if "1024" in face_restoration:
784
+ arch = "GPEN-1024"
785
+ resolution = 1024
786
+ elif "2048" in face_restoration:
787
+ arch = "GPEN-2048"
788
+ resolution = 2048
789
+
790
+ self.face_enhancer = GFPGANer(model_path=model_path, upscale=self.scale, arch=arch, channel_multiplier=channel_multiplier, model_rootpath=model_rootpath, det_model=face_detection, resolution=resolution)
791
+
792
+
793
+ def inference(self, gallery, face_restoration, upscale_model, scale: float, face_detection, face_detection_threshold: any, face_detection_only_center: bool, outputWithModelName: bool, save_as_png: bool, progress=gr.Progress()):
794
+ try:
795
+ if not gallery or (not face_restoration and not upscale_model):
796
+ raise ValueError("Invalid parameter setting")
797
+
798
+ gallery_len = len(gallery)
799
+ print(face_restoration, upscale_model, scale, f"gallery length: {gallery_len}")
800
+
801
+ timer = Timer() # Create a timer
802
+ self.scale = scale
803
+
804
+ progressTotal = gallery_len + 1
805
+ progressRatio = 0.5 if upscale_model and face_restoration else 1
806
+ print(f"progressRatio: {progressRatio}")
807
+ current_progress = 0
808
+ progress(0, desc="Initializing models...")
809
+ if upscale_model:
810
+ self.initBGUpscaleModel(upscale_model)
811
+ current_progress += progressRatio/progressTotal;
812
+ progress(current_progress, desc="BG upscale model initialized.")
813
+ timer.checkpoint(f"Initialize BG upscale model")
814
+
815
+ if face_restoration:
816
+ self.initFaceEnhancerModel(face_restoration, face_detection)
817
+ current_progress += progressRatio/progressTotal;
818
+ progress(current_progress, desc="Face enhancer model initialized.")
819
+ timer.checkpoint(f"Initialize face enhancer model")
820
+
821
+ timer.report()
822
+
823
+ if not outputWithModelName:
824
+ self.modelInUse = ""
825
+
826
+ files = []
827
+ # Create zip files for each output type
828
+ unique_id = str(int(time.time())) # Use timestamp for uniqueness
829
+ zip_cropf_path = f"output/{unique_id}_cropped_faces{self.modelInUse}.zip"
830
+ zipf_cropf = zipfile.ZipFile(zip_cropf_path, 'w', zipfile.ZIP_DEFLATED)
831
+ zip_restoref_path = f"output/{unique_id}_restored_faces{self.modelInUse}.zip"
832
+ zipf_restoref = zipfile.ZipFile(zip_restoref_path, 'w', zipfile.ZIP_DEFLATED)
833
+ zip_cmp_path = f"output/{unique_id}_cmp{self.modelInUse}.zip"
834
+ zipf_cmp = zipfile.ZipFile(zip_cmp_path, 'w', zipfile.ZIP_DEFLATED)
835
+ zip_restore_path = f"output/{unique_id}_restored_images{self.modelInUse}.zip"
836
+ zipf_restore = zipfile.ZipFile(zip_restore_path, 'w', zipfile.ZIP_DEFLATED)
837
+
838
+ is_auto_split_upscale = True
839
+ # Dictionary to track counters for each filename
840
+ name_counters = defaultdict(int)
841
+ for gallery_idx, value in enumerate(gallery):
842
+ img_path = None
843
+ try:
844
+ if value is None or not value:
845
+ print(f"Warning: Invalid gallery item at index {gallery_idx}. Skipping.")
846
+ continue
847
+ img_path = str(value[0]) # value is often a list/tuple like [filepath, caption]
848
+ img_name = os.path.basename(img_path)
849
+ basename, extension = os.path.splitext(img_name)
850
+ # Increment the counter for the current name if it appears multiple times
851
+ name_counters[img_name] += 1
852
+ if name_counters[img_name] > 1:
853
+ basename = f"{basename}_{name_counters[img_name] - 1:02d}"
854
+
855
+ img_cv2 = cv2.imdecode(np.fromfile(img_path, np.uint8), cv2.IMREAD_UNCHANGED) # numpy.ndarray
856
+
857
+ if img_cv2 is None:
858
+ print(f"Warning: Could not read or decode image '{img_path}'. Skipping this image.")
859
+ # Skip this iteration and process the next image
860
+ continue
861
+
862
+ img_mode = "RGBA" if len(img_cv2.shape) == 3 and img_cv2.shape[2] == 4 else None
863
+ if len(img_cv2.shape) == 2: # for gray inputs
864
+ img_cv2 = cv2.cvtColor(img_cv2, cv2.COLOR_GRAY2BGR)
865
+ print(f"> Processing image {gallery_idx:02d}, Shape: {img_cv2.shape}")
866
+
867
+ bg_upsample_img = None
868
+ if upscale_model and self.realesrganer and hasattr(self.realesrganer, "enhance"):
869
+ bg_upsample_img, _ = auto_split_upscale(img_cv2, self.realesrganer.enhance, self.scale) if is_auto_split_upscale else self.realesrganer.enhance(img_cv2, outscale=self.scale)
870
+ current_progress += progressRatio/progressTotal;
871
+ progress(current_progress, desc=f"Image {gallery_idx:02d}: Background upscaling...")
872
+ timer.checkpoint(f"Image {gallery_idx:02d}: Background upscale section")
873
+
874
+ if face_restoration and self.face_enhancer:
875
+ cropped_faces, restored_aligned, bg_upsample_img = self.face_enhancer.enhance(img_cv2, has_aligned=False, only_center_face=face_detection_only_center, paste_back=True, bg_upsample_img=bg_upsample_img, eye_dist_threshold=face_detection_threshold)
876
+ # save faces
877
+ if cropped_faces and restored_aligned:
878
+ for idx, (cropped_face, restored_face) in enumerate(zip(cropped_faces, restored_aligned)):
879
+ # save cropped face
880
+ save_crop_path = f"output/{basename}_{idx:02d}_cropped_faces{self.modelInUse}.png"
881
+ self.imwriteUTF8(save_crop_path, cropped_face)
882
+ zipf_cropf.write(save_crop_path, arcname=os.path.basename(save_crop_path))
883
+ # save restored face
884
+ save_restore_path = f"output/{basename}_{idx:02d}_restored_faces{self.modelInUse}.png"
885
+ self.imwriteUTF8(save_restore_path, restored_face)
886
+ zipf_restoref.write(save_restore_path, arcname=os.path.basename(save_restore_path))
887
+ # save comparison image
888
+ save_cmp_path = f"output/{basename}_{idx:02d}_cmp{self.modelInUse}.png"
889
+ cmp_img = np.concatenate((cropped_face, restored_face), axis=1)
890
+ self.imwriteUTF8(save_cmp_path, cmp_img)
891
+ zipf_cmp.write(save_cmp_path, arcname=os.path.basename(save_cmp_path))
892
+
893
+ files.append(save_crop_path)
894
+ files.append(save_restore_path)
895
+ files.append(save_cmp_path)
896
+ current_progress += progressRatio/progressTotal;
897
+ progress(current_progress, desc=f"Image {gallery_idx:02d}: Face enhancement...")
898
+ timer.checkpoint(f"Image {gallery_idx:02d}: Face enhancer section")
899
+
900
+ restored_img = bg_upsample_img
901
+ timer.report() # Report time for this image
902
+
903
+ # Handle cases where image processing might still result in None
904
+ if restored_img is None:
905
+ print(f"Warning: Processing resulted in no image for '{img_path}'. Skipping output.")
906
+ continue
907
+
908
+ # Determine the file extension for the output image based on user preference and image properties.
909
+ if save_as_png:
910
+ # Force PNG output for the best quality, as requested by the user.
911
+ final_extension = ".png"
912
+ else:
913
+ # Use original logic: PNG for images with an alpha channel (RGBA), otherwise use the original extension or default to jpg.
914
+ final_extension = ".png" if img_mode == "RGBA" else (extension if extension else ".jpg")
915
+ save_path = f"output/{basename}{self.modelInUse}{final_extension}"
916
+ self.imwriteUTF8(save_path, restored_img)
917
+ zipf_restore.write(save_path, arcname=os.path.basename(save_path))
918
+
919
+ restored_img = cv2.cvtColor(restored_img, cv2.COLOR_BGR2RGB)
920
+ files.append(save_path)
921
+ except RuntimeError as error:
922
+ print(f"Runtime Error while processing image {gallery_idx} ({img_path or 'unknown path'}): {error}")
923
+ print(traceback.format_exc())
924
+ except Exception as general_error:
925
+ print(f"An unexpected error occurred while processing image {gallery_idx} ({img_path or 'unknown path'}): {general_error}")
926
+ print(traceback.format_exc())
927
+ # Can still choose to continue to process the next image
928
+ continue
929
+
930
+ progress(1, desc=f"Processing complete.")
931
+ timer.report_all() # Print all recorded times for the whole batch
932
+ # Close zip files
933
+ zipf_cropf.close()
934
+ zipf_restoref.close()
935
+ zipf_cmp.close()
936
+ zipf_restore.close()
937
+ except Exception as error:
938
+ print(f"Global exception occurred: {error}")
939
+ print(traceback.format_exc())
940
+ return None, None
941
+ finally:
942
+ if hasattr(self, 'face_enhancer') and self.face_enhancer:
943
+ self.face_enhancer._cleanup()
944
+ # Free GPU memory and clean up resources
945
+ if torch.cuda.is_available():
946
+ torch.cuda.empty_cache()
947
+ gc.collect()
948
+
949
+ return files, [zip_cropf_path, zip_restoref_path, zip_cmp_path, zip_restore_path] if face_restoration else [zip_restore_path]
950
+
951
+
952
+ def find_max_numbers(self, state_dict, findkeys):
953
+ if isinstance(findkeys, str):
954
+ findkeys = [findkeys]
955
+ max_values = defaultdict(lambda: None)
956
+ patterns = {findkey: re.compile(rf"^{re.escape(findkey)}\.(\d+)\.") for findkey in findkeys}
957
+
958
+ for key in state_dict:
959
+ for findkey, pattern in patterns.items():
960
+ if match := pattern.match(key):
961
+ num = int(match.group(1))
962
+ max_values[findkey] = max(num, max_values[findkey] if max_values[findkey] is not None else num)
963
+
964
+ return tuple(max_values[findkey] for findkey in findkeys) if len(findkeys) > 1 else max_values[findkeys[0]]
965
+
966
+ def find_divisor_for_quotient(self, a: int, c: int):
967
+ """
968
+ Returns a number `b` such that `a // b == c`.
969
+ If `b` is an integer, return it as an `int`, otherwise return a `float`.
970
+ """
971
+ if c == 0:
972
+ raise ValueError("c cannot be zero to avoid division by zero.")
973
+
974
+ b_float = a / c
975
+
976
+ # Check if b is an integer
977
+ if b_float.is_integer():
978
+ return int(b_float)
979
+
980
+ # Try using ceil and floor
981
+ ceil_b = math.ceil(b_float)
982
+ floor_b = math.floor(b_float)
983
+
984
+ if a // ceil_b == c:
985
+ return ceil_b if ceil_b == b_float else float(ceil_b)
986
+ if a // floor_b == c:
987
+ return floor_b if floor_b == b_float else float(floor_b)
988
+
989
+ # account for rounding errors
990
+ if c == a // b_float:
991
+ return b_float
992
+ if c == a // (b_float - 0.01):
993
+ return b_float - 0.01
994
+ if c == a // (b_float + 0.01):
995
+ return b_float + 0.01
996
+
997
+ raise ValueError(f"Could not find a number b such that a // b == c. a={a}, c={c}")
998
+
999
+ def imwriteUTF8(self, save_path, image): # `cv2.imwrite` does not support writing files to UTF-8 file paths.
1000
+ img_name = os.path.basename(save_path)
1001
+ _, extension = os.path.splitext(img_name)
1002
+ is_success, im_buf_arr = cv2.imencode(extension, image)
1003
+ if (is_success): im_buf_arr.tofile(save_path)
1004
+
1005
+ class Timer:
1006
+ def __init__(self):
1007
+ self.start_time = time.perf_counter() # Record the start time
1008
+ self.checkpoints = [("Start", self.start_time)] # Store checkpoints
1009
+
1010
+ def checkpoint(self, label="Checkpoint"):
1011
+ """Record a checkpoint with a given label."""
1012
+ now = time.perf_counter()
1013
+ self.checkpoints.append((label, now))
1014
+
1015
+ def report(self, is_clear_checkpoints = True):
1016
+ # Determine the max label width for alignment
1017
+ max_label_length = max(len(label) for label, _ in self.checkpoints)
1018
+
1019
+ prev_time = self.checkpoints[0][1]
1020
+ for label, curr_time in self.checkpoints[1:]:
1021
+ elapsed = curr_time - prev_time
1022
+ print(f"{label.ljust(max_label_length)}: {elapsed:.3f} seconds")
1023
+ prev_time = curr_time
1024
+
1025
+ if is_clear_checkpoints:
1026
+ self.checkpoints.clear()
1027
+ self.checkpoint() # Store checkpoints
1028
+
1029
+ def report_all(self):
1030
+ """Print all recorded checkpoints and total execution time with aligned formatting."""
1031
+ print("\n> Execution Time Report:")
1032
+
1033
+ # Determine the max label width for alignment
1034
+ max_label_length = max(len(label) for label, _ in self.checkpoints) if len(self.checkpoints) > 0 else 0
1035
+
1036
+ prev_time = self.start_time
1037
+ for label, curr_time in self.checkpoints[1:]:
1038
+ elapsed = curr_time - prev_time
1039
+ print(f"{label.ljust(max_label_length)}: {elapsed:.3f} seconds")
1040
+ prev_time = curr_time
1041
+
1042
+ total_time = self.checkpoints[-1][1] - self.start_time
1043
+ print(f"{'Total Execution Time'.ljust(max_label_length)}: {total_time:.3f} seconds\n")
1044
+
1045
+ self.checkpoints.clear()
1046
+
1047
+ def restart(self):
1048
+ self.start_time = time.perf_counter() # Record the start time
1049
+ self.checkpoints = [("Start", self.start_time)] # Store checkpoints
1050
+
1051
+
1052
+ def get_selection_from_gallery(selected_state: gr.SelectData):
1053
+ """
1054
+ Extracts the selected image path and caption from the gallery selection state.
1055
+
1056
+ Args:
1057
+ selected_state (gr.SelectData): The selection state from a Gradio gallery,
1058
+ containing information about the selected image.
1059
+
1060
+ Returns:
1061
+ tuple: A tuple containing:
1062
+ - str: The file path of the selected image.
1063
+ - str: The caption of the selected image.
1064
+ If `selected_state` is None or invalid, it returns `None`.
1065
+ """
1066
+ if not selected_state:
1067
+ return selected_state
1068
+
1069
+ return (selected_state.value["image"]["path"], selected_state.value["caption"])
1070
+
1071
+ def limit_gallery(gallery):
1072
+ """
1073
+ Ensures the gallery does not exceed input_images_limit.
1074
+
1075
+ Args:
1076
+ gallery (list): Current gallery images.
1077
+
1078
+ Returns:
1079
+ list: Trimmed gallery with a maximum of input_images_limit images.
1080
+ """
1081
+ return gallery[:input_images_limit] if input_images_limit > 0 and gallery else gallery
1082
+
1083
+ def append_gallery(gallery: list, image: str):
1084
+ """
1085
+ Append a single image to the gallery while respecting input_images_limit.
1086
+
1087
+ Parameters:
1088
+ - gallery (list): Existing list of images. If None, initializes an empty list.
1089
+ - image (str): The image to be added. If None or empty, no action is taken.
1090
+
1091
+ Returns:
1092
+ - list: Updated gallery.
1093
+ """
1094
+ if gallery is None:
1095
+ gallery = []
1096
+ if not image:
1097
+ return gallery, None
1098
+
1099
+ if input_images_limit == -1 or len(gallery) < input_images_limit:
1100
+ gallery.append(image)
1101
+
1102
+ return gallery, None
1103
+
1104
+
1105
+ def extend_gallery(gallery: list, images):
1106
+ """
1107
+ Extend the gallery with new images while respecting the input_images_limit.
1108
+
1109
+ Parameters:
1110
+ - gallery (list): Existing list of images. If None, initializes an empty list.
1111
+ - images (list): New images to be added. If None, defaults to an empty list.
1112
+
1113
+ Returns:
1114
+ - list: Updated gallery with the new images added.
1115
+ """
1116
+ if gallery is None:
1117
+ gallery = []
1118
+ if not images:
1119
+ return gallery
1120
+
1121
+ # Add new images to the gallery
1122
+ gallery.extend(images)
1123
+
1124
+ # Trim gallery to the specified limit, if applicable
1125
+ if input_images_limit > 0:
1126
+ gallery = gallery[:input_images_limit]
1127
+
1128
+ return gallery
1129
+
1130
+ def remove_image_from_gallery(gallery: list, selected_image: str):
1131
+ """
1132
+ Removes a selected image from the gallery if it exists.
1133
+
1134
+ Args:
1135
+ gallery (list): The current list of images in the gallery.
1136
+ selected_image (str): The image to be removed, represented as a string
1137
+ that needs to be parsed into a tuple.
1138
+
1139
+ Returns:
1140
+ list: The updated gallery after removing the selected image.
1141
+ """
1142
+ if not gallery or not selected_image:
1143
+ return gallery
1144
+
1145
+ selected_image = ast.literal_eval(selected_image) # Use ast.literal_eval to parse text into a tuple in remove_image_from_gallery.
1146
+ # Remove the selected image from the gallery
1147
+ if selected_image in gallery:
1148
+ gallery.remove(selected_image)
1149
+ return gallery
1150
+
1151
+ def main():
1152
+ if torch.cuda.is_available():
1153
+ # Sets the VRAM limit for the GPU. Fine-tune VRAM usage.
1154
+ # Higher values allow larger image tiles to be processed at once, which is faster. If you see the 'depth' in the console log increase frequently, try raising this value.
1155
+ # Lower it for complex models that need more memory overhead.
1156
+ # This setting is locked in once processing starts. To apply a new value, you must restart the entire application.
1157
+ torch.cuda.set_per_process_memory_fraction(0.900, device='cuda:0')
1158
+ # set torch options to avoid get black image for RTX16xx card
1159
+ # https://github.com/CompVis/stable-diffusion/issues/69#issuecomment-1260722801
1160
+ torch.backends.cudnn.enabled = True
1161
+ torch.backends.cudnn.benchmark = True
1162
+ # Ensure the target directory exists
1163
+ os.makedirs('output', exist_ok=True)
1164
+
1165
+ title = "Image Upscaling & Restoration using GFPGAN / RestoreFormerPlusPlus / CodeFormer / GPEN Algorithm"
1166
+ description = r"""
1167
+ <a href='https://github.com/TencentARC/GFPGAN' target='_blank'><b>GFPGAN: Towards Real-World Blind Face Restoration and Upscalling of the image with a Generative Facial Prior</b></a>. <br>
1168
+ <a href='https://github.com/wzhouxiff/RestoreFormerPlusPlus' target='_blank'><b>RestoreFormer++: Towards Real-World Blind Face Restoration from Undegraded Key-Value Pairs</b></a>. <br>
1169
+ <a href='https://github.com/sczhou/CodeFormer' target='_blank'><b>CodeFormer: Towards Robust Blind Face Restoration with Codebook Lookup Transformer (NeurIPS 2022)</b></a>. <br>
1170
+ <a href='https://github.com/yangxy/GPEN' target='_blank'><b>GPEN: GAN Prior Embedded Network for Blind Face Restoration in the Wild</b></a>. <br>
1171
+ <br>
1172
+ Practically, the aforementioned algorithm is used to restore your **old photos** or improve **AI-generated faces**.<br>
1173
+ To use it, simply just upload the concerned image.<br>
1174
+ """
1175
+ # Custom CSS to set the height of the gr.Dropdown menu
1176
+ css = """
1177
+ ul.options {
1178
+ max-height: 500px !important; /* Set the maximum height of the dropdown menu */
1179
+ overflow-y: auto !important; /* Enable vertical scrolling if the content exceeds the height */
1180
+ }
1181
+ div.progress-level div.progress-level-inner {
1182
+ text-align: left !important;
1183
+ width: 55.5% !important;
1184
+ }
1185
+ """
1186
+
1187
+ upscale = Upscale()
1188
+
1189
+ rows = []
1190
+ tmptype = None
1191
+ upscale_model_tables = []
1192
+ for key, _ in typed_upscale_models.items():
1193
+ upscale_type, upscale_model = key.split(", ", 1)
1194
+ if tmptype and tmptype != upscale_type:#RRDB ESRGAN
1195
+ speed = "Fast" if tmptype == "SRVGG" else ("Slow" if any(value == tmptype for value in ("DAT", "HAT", "DRCT", "ATD", "SRFormer")) else "Normal")
1196
+ upscale_model_header = f"| Upscale Model | Info, Type: {tmptype}, Model execution speed: {speed} | Download URL |\n|------------|------|--------------|"
1197
+ upscale_model_tables.append(upscale_model_header + "\n" + "\n".join(rows))
1198
+ rows.clear()
1199
+ tmptype = upscale_type
1200
+ value = upscale_models[upscale_model]
1201
+ row = f"| [{upscale_model}]({value[1]}) | " + value[2].replace("\n", "<br>") + " | [download]({value[0]}) |"
1202
+ rows.append(row)
1203
+ speed = "Fast" if tmptype == "SRVGG" else ("Slow" if any(value == tmptype for value in ("DAT", "HAT", "DRCT", "ATD", "SRFormer")) else "Normal")
1204
+ upscale_model_header = f"| Upscale Model Name | Info, Type: {tmptype}, Model execution speed: {speed} | Download URL |\n|------------|------|--------------|"
1205
+ upscale_model_tables.append(upscale_model_header + "\n" + "\n".join(rows))
1206
+
1207
+ with gr.Blocks(title = title, css = css) as demo:
1208
+ gr.Markdown(value=f"<h1 style=\"text-align:center;\">{title}</h1><br>{description}")
1209
+ with gr.Row():
1210
+ with gr.Column(variant="panel"):
1211
+ submit = gr.Button(value="Submit", variant="primary", size="lg")
1212
+ # Create an Image component for uploading images
1213
+ input_image = gr.Image(label="Upload an Image or clicking paste from clipboard button", type="filepath", format="png", height=150)
1214
+ with gr.Row():
1215
+ upload_button = gr.UploadButton("Upload multiple images", file_types=["image"], file_count="multiple", size="sm")
1216
+ remove_button = gr.Button("Remove Selected Image", size="sm")
1217
+ input_gallery = gr.Gallery(columns=5, rows=5, show_share_button=False, interactive=True, height="500px", label="Gallery that displaying a grid of images" + (f"(The online environment image limit is {input_images_limit})" if input_images_limit > 0 else ""))
1218
+ face_model = gr.Dropdown([None]+list(face_models.keys()), type="value", value='GFPGANv1.4.pth', label='Face Restoration version', info="Face Restoration and RealESR can be freely combined in different ways, or one can be set to \"None\" to use only the other model. Face Restoration is primarily used for face restoration in real-life images, while RealESR serves as a background restoration model.")
1219
+ upscale_model = gr.Dropdown([None]+list(typed_upscale_models.keys()), type="value", value='SRVGG, realesr-general-x4v3.pth', label='UpScale version')
1220
+ upscale_scale = gr.Number(label="Rescaling factor", value=4)
1221
+ face_detection = gr.Dropdown(["retinaface_resnet50", "YOLOv5l", "YOLOv5n"], type="value", value="retinaface_resnet50", label="Face Detection type")
1222
+ face_detection_threshold = gr.Number(label="Face eye dist threshold", value=10, info="A threshold to filter out faces with too small an eye distance (e.g., side faces).")
1223
+ face_detection_only_center = gr.Checkbox(value=False, label="Face detection only center", info="If set to True, only the face closest to the center of the image will be kept.")
1224
+ with_model_name = gr.Checkbox(label="Output image files name with model name", value=True)
1225
+ # Add a checkbox to always save the output as a PNG file for the best quality.
1226
+ save_as_png = gr.Checkbox(label="Always save output as PNG", value=True, info="If enabled, all output images will be saved in PNG format to ensure the best quality. If disabled, the format will be determined automatically (PNG for images with transparency, otherwise JPG).")
1227
+
1228
+ # Define the event listener to add the uploaded image to the gallery
1229
+ input_image.change(append_gallery, inputs=[input_gallery, input_image], outputs=[input_gallery, input_image])
1230
+ # When the upload button is clicked, add the new images to the gallery
1231
+ upload_button.upload(extend_gallery, inputs=[input_gallery, upload_button], outputs=input_gallery)
1232
+ # Event to update the selected image when an image is clicked in the gallery
1233
+ selected_image = gr.Textbox(label="Selected Image", visible=False)
1234
+ input_gallery.select(get_selection_from_gallery, inputs=None, outputs=selected_image)
1235
+ # Trigger update when gallery changes
1236
+ input_gallery.change(limit_gallery, input_gallery, input_gallery)
1237
+ # Event to remove a selected image from the gallery
1238
+ remove_button.click(remove_image_from_gallery, inputs=[input_gallery, selected_image], outputs=input_gallery)
1239
+
1240
+ with gr.Row():
1241
+ clear = gr.ClearButton(
1242
+ components=[
1243
+ input_gallery,
1244
+ face_model,
1245
+ upscale_model,
1246
+ upscale_scale,
1247
+ face_detection,
1248
+ face_detection_threshold,
1249
+ face_detection_only_center,
1250
+ with_model_name,
1251
+ save_as_png,
1252
+ ], variant="secondary", size="lg",)
1253
+ with gr.Column(variant="panel"):
1254
+ gallerys = gr.Gallery(type="filepath", label="Output (The whole image)", format="png")
1255
+ outputs = gr.File(label="Download the output ZIP file")
1256
+ with gr.Row(variant="panel"):
1257
+ # Generate output array
1258
+ output_arr = []
1259
+ for file_name in example_list:
1260
+ output_arr.append([file_name,])
1261
+ gr.Examples(output_arr, inputs=[input_image,], examples_per_page=20)
1262
+ with gr.Row(variant="panel"):
1263
+ # Convert to Markdown table
1264
+ header = "| Face Model Name | Info | Download URL |\n|------------|------|--------------|"
1265
+ rows = [
1266
+ f"| [{key}]({value[1]}) | " + value[2].replace("\n", "<br>") + f" | [download]({value[0]}) |"
1267
+ for key, value in face_models.items()
1268
+ ]
1269
+ markdown_table = header + "\n" + "\n".join(rows)
1270
+ gr.Markdown(value=markdown_table)
1271
+
1272
+ for table in upscale_model_tables:
1273
+ with gr.Row(variant="panel"):
1274
+ gr.Markdown(value=table)
1275
+
1276
+ submit.click(
1277
+ upscale.inference,
1278
+ inputs=[
1279
+ input_gallery,
1280
+ face_model,
1281
+ upscale_model,
1282
+ upscale_scale,
1283
+ face_detection,
1284
+ face_detection_threshold,
1285
+ face_detection_only_center,
1286
+ with_model_name,
1287
+ save_as_png,
1288
+ ],
1289
+ outputs=[gallerys, outputs],
1290
+ )
1291
+
1292
+ demo.queue(default_concurrency_limit=1)
1293
+ demo.launch(inbrowser=True)
1294
+
1295
+
1296
+ if __name__ == "__main__":
1297
+ parser = argparse.ArgumentParser()
1298
+ parser.add_argument("--input_images_limit", type=int, default=5)
1299
+ args = parser.parse_args()
1300
+ input_images_limit = args.input_images_limit
1301
+ main()
packages.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ ffmpeg
2
+ libsm6
3
+ libxext6
requirements.txt ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --extra-index-url https://download.pytorch.org/whl/cu128
2
+
3
+ gradio
4
+
5
+ basicsr @ git+https://github.com/avan06/BasicSR
6
+ facexlib @ git+https://github.com/avan06/facexlib
7
+ gfpgan @ git+https://github.com/avan06/GFPGAN
8
+
9
+ numpy
10
+ opencv-python
11
+
12
+ torch
13
+ torchvision
14
+
15
+ scipy
16
+ tqdm
17
+ lmdb
18
+ pyyaml
19
+ yapf
20
+
21
+ image_gen_aux @ git+https://github.com/huggingface/image_gen_aux
22
+ gdown # supports downloading the large file from Google Drive
utils/dataops.py ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # The file source is from the [ESRGAN](https://github.com/xinntao/ESRGAN) project
4
+ # forked by authors [joeyballentine](https://github.com/joeyballentine/ESRGAN) and [BlueAmulet](https://github.com/BlueAmulet/ESRGAN).
5
+
6
+ import gc
7
+
8
+ import numpy as np
9
+ import torch
10
+
11
+
12
+ def bgr_to_rgb(image: torch.Tensor) -> torch.Tensor:
13
+ # flip image channels
14
+ # https://github.com/pytorch/pytorch/issues/229
15
+ out: torch.Tensor = image.flip(-3)
16
+ # out: torch.Tensor = image[[2, 1, 0], :, :] #RGB to BGR #may be faster
17
+ return out
18
+
19
+
20
+ def rgb_to_bgr(image: torch.Tensor) -> torch.Tensor:
21
+ # same operation as bgr_to_rgb(), flip image channels
22
+ return bgr_to_rgb(image)
23
+
24
+
25
+ def bgra_to_rgba(image: torch.Tensor) -> torch.Tensor:
26
+ out: torch.Tensor = image[[2, 1, 0, 3], :, :]
27
+ return out
28
+
29
+
30
+ def rgba_to_bgra(image: torch.Tensor) -> torch.Tensor:
31
+ # same operation as bgra_to_rgba(), flip image channels
32
+ return bgra_to_rgba(image)
33
+
34
+
35
+ def auto_split_upscale(
36
+ lr_img: np.ndarray,
37
+ upscale_function,
38
+ scale: int = 4,
39
+ overlap: int = 32,
40
+ # A heuristic to proactively split tiles that are too large, avoiding a CUDA error.
41
+ # The default (2048*2048) is a conservative value for moderate VRAM (e.g., 8-12GB).
42
+ # Adjust this based on your GPU and model's memory footprint.
43
+ max_tile_pixels: int = 4194304, # Default: 2048 * 2048 pixels
44
+ # Internal parameters for recursion state. Do not set these manually.
45
+ known_max_depth: int = None,
46
+ current_depth: int = 1,
47
+ current_tile: int = 1, # Tracks the current tile being processed
48
+ total_tiles: int = 1, # Total number of tiles at this depth level
49
+ ):
50
+ # --- Step 0: Handle CPU-only environment ---
51
+ # The entire splitting logic is designed to overcome GPU VRAM limitations.
52
+ # If no CUDA-enabled GPU is present, this logic is unnecessary and adds overhead.
53
+ # Therefore, we process the image in one go on the CPU.
54
+ if not torch.cuda.is_available():
55
+ # Note: This assumes the image fits into system RAM, which is usually the case.
56
+ result, _ = upscale_function(lr_img, scale)
57
+ # The conceptual depth is 1 since no splitting was performed.
58
+ return result, 1
59
+
60
+ """
61
+ Automatically splits an image into tiles for upscaling to avoid CUDA out-of-memory errors.
62
+ It uses a combination of a pixel-count heuristic and reactive error handling to find the
63
+ optimal processing depth, then applies this depth to all subsequent tiles.
64
+ """
65
+ input_h, input_w, input_c = lr_img.shape
66
+
67
+ # --- Step 1: Decide if we should ATTEMPT to upscale or MUST split ---
68
+ # We must split if:
69
+ # A) The tile is too large based on our heuristic, and we don't have a known working depth yet.
70
+ # B) We have a known working depth from a sibling tile, but we haven't recursed deep enough to reach it yet.
71
+ must_split = (known_max_depth is None and (input_h * input_w) > max_tile_pixels) or \
72
+ (known_max_depth is not None and current_depth < known_max_depth)
73
+
74
+ if not must_split:
75
+ # If we are not forced to split, let's try to upscale the current tile.
76
+ try:
77
+ print(f"auto_split_upscale depth: {current_depth}", end=" ", flush=True)
78
+ result, _ = upscale_function(lr_img, scale)
79
+ # SUCCESS! The upscale worked at this depth.
80
+ print(f"progress: {current_tile}/{total_tiles}")
81
+ # Return the result and the current depth, which is now the "known_max_depth".
82
+ return result, current_depth
83
+ except RuntimeError as e:
84
+ # Check to see if its actually the CUDA out of memory error
85
+ if "CUDA" in str(e):
86
+ # OOM ERROR. Our heuristic was too optimistic. This depth is not viable.
87
+ print("RuntimeError: CUDA out of memory...")
88
+ # Clean up VRAM and proceed to the splitting logic below.
89
+ torch.cuda.empty_cache()
90
+ gc.collect()
91
+ else:
92
+ # A different runtime error occurred, so we should not suppress it.
93
+ raise RuntimeError(e)
94
+ # If an OOM error occurred, flow continues to the splitting section.
95
+
96
+ # --- Step 2: If we reached here, we MUST split the image ---
97
+
98
+ # Safety break to prevent infinite recursion if something goes wrong.
99
+ if current_depth > 10:
100
+ raise RuntimeError("Maximum recursion depth exceeded. Check max_tile_pixels or model requirements.")
101
+
102
+ # Prepare parameters for the next level of recursion.
103
+ next_depth = current_depth + 1
104
+ new_total_tiles = total_tiles * 4
105
+ base_tile_for_next_level = (current_tile - 1) * 4
106
+
107
+ # Announce the split only when it's happening.
108
+ print(f"Splitting tile at depth {current_depth} into 4 tiles for depth {next_depth}.")
109
+
110
+ # Split the image into 4 quadrants with overlap.
111
+ top_left = lr_img[: input_h // 2 + overlap, : input_w // 2 + overlap, :]
112
+ top_right = lr_img[: input_h // 2 + overlap, input_w // 2 - overlap :, :]
113
+ bottom_left = lr_img[input_h // 2 - overlap :, : input_w // 2 + overlap, :]
114
+ bottom_right = lr_img[input_h // 2 - overlap :, input_w // 2 - overlap :, :]
115
+
116
+ # Recursively process each quadrant.
117
+ # Process the first quadrant to discover the safe depth.
118
+ # The first quadrant (top_left) will "discover" the correct processing depth.
119
+ # Pass the current `known_max_depth` down.
120
+ top_left_rlt, discovered_depth = auto_split_upscale(
121
+ top_left, upscale_function, scale=scale, overlap=overlap,
122
+ max_tile_pixels=max_tile_pixels,
123
+ known_max_depth=known_max_depth,
124
+ current_depth=next_depth,
125
+ current_tile=base_tile_for_next_level + 1,
126
+ total_tiles=new_total_tiles,
127
+ )
128
+ # Once the depth is discovered, pass it to the other quadrants to avoid redundant checks.
129
+ top_right_rlt, _ = auto_split_upscale(
130
+ top_right, upscale_function, scale=scale, overlap=overlap,
131
+ max_tile_pixels=max_tile_pixels,
132
+ known_max_depth=discovered_depth,
133
+ current_depth=next_depth,
134
+ current_tile=base_tile_for_next_level + 2,
135
+ total_tiles=new_total_tiles,
136
+ )
137
+ bottom_left_rlt, _ = auto_split_upscale(
138
+ bottom_left, upscale_function, scale=scale, overlap=overlap,
139
+ max_tile_pixels=max_tile_pixels,
140
+ known_max_depth=discovered_depth,
141
+ current_depth=next_depth,
142
+ current_tile=base_tile_for_next_level + 3,
143
+ total_tiles=new_total_tiles,
144
+ )
145
+ bottom_right_rlt, _ = auto_split_upscale(
146
+ bottom_right, upscale_function, scale=scale, overlap=overlap,
147
+ max_tile_pixels=max_tile_pixels,
148
+ known_max_depth=discovered_depth,
149
+ current_depth=next_depth,
150
+ current_tile=base_tile_for_next_level + 4,
151
+ total_tiles=new_total_tiles,
152
+ )
153
+
154
+ # --- Step 3: Stitch the results back together ---
155
+ # Reassemble the upscaled quadrants into a single image.
156
+ out_h = input_h * scale
157
+ out_w = input_w * scale
158
+
159
+ # Create an empty output image
160
+ output_img = np.zeros((out_h, out_w, input_c), np.uint8)
161
+
162
+ # Fill the output image, removing the overlap regions to prevent artifacts
163
+ output_img[: out_h // 2, : out_w // 2, :] = top_left_rlt[: out_h // 2, : out_w // 2, :]
164
+ output_img[: out_h // 2, -out_w // 2 :, :] = top_right_rlt[: out_h // 2, -out_w // 2 :, :]
165
+ output_img[-out_h // 2 :, : out_w // 2, :] = bottom_left_rlt[-out_h // 2 :, : out_w // 2, :]
166
+ output_img[-out_h // 2 :, -out_w // 2 :, :] = bottom_right_rlt[-out_h // 2 :, -out_w // 2 :, :]
167
+
168
+ return output_img, discovered_depth
webui.bat ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @echo off
2
+
3
+ :: The original source of the webui.bat file is stable-diffusion-webui
4
+ :: Modified and enhanced by Gemini with features for venv management and requirements handling.
5
+
6
+ :: --------- Configuration ---------
7
+ set COMMANDLINE_ARGS=--input_images_limit -1
8
+ :: Define the name of the Launch application
9
+ set APPLICATION_NAME=app.py
10
+ :: Define the name of the virtual environment directory
11
+ set VENV_NAME=venv
12
+ :: Set to 1 to always attempt to update packages from requirements.txt on every launch
13
+ set ALWAYS_UPDATE_REQS=0
14
+ :: ---------------------------------
15
+
16
+
17
+ :: Set PYTHON executable if not already defined
18
+ if not defined PYTHON (set PYTHON=python)
19
+ :: Set VENV_DIR using VENV_NAME if not already defined
20
+ if not defined VENV_DIR (set "VENV_DIR=%~dp0%VENV_NAME%")
21
+
22
+ mkdir tmp 2>NUL
23
+
24
+ :: Check if Python is callable
25
+ %PYTHON% -c "" >tmp/stdout.txt 2>tmp/stderr.txt
26
+ if %ERRORLEVEL% == 0 goto :check_pip
27
+ echo Couldn't launch python
28
+ goto :show_stdout_stderr
29
+
30
+ :check_pip
31
+ :: Check if pip is available
32
+ %PYTHON% -mpip --help >tmp/stdout.txt 2>tmp/stderr.txt
33
+ if %ERRORLEVEL% == 0 goto :start_venv
34
+ :: If pip is not available and PIP_INSTALLER_LOCATION is set, try to install pip
35
+ if "%PIP_INSTALLER_LOCATION%" == "" goto :show_stdout_stderr
36
+ %PYTHON% "%PIP_INSTALLER_LOCATION%" >tmp/stdout.txt 2>tmp/stderr.txt
37
+ if %ERRORLEVEL% == 0 goto :start_venv
38
+ echo Couldn't install pip
39
+ goto :show_stdout_stderr
40
+
41
+ :start_venv
42
+ :: Skip venv creation/activation if VENV_DIR is explicitly set to "-"
43
+ if ["%VENV_DIR%"] == ["-"] goto :skip_venv_entirely
44
+ :: Skip venv creation/activation if SKIP_VENV is set to "1"
45
+ if ["%SKIP_VENV%"] == ["1"] goto :skip_venv_entirely
46
+
47
+ :: Check if the venv already exists by looking for Python.exe in its Scripts directory
48
+ dir "%VENV_DIR%\Scripts\Python.exe" >tmp/stdout.txt 2>tmp/stderr.txt
49
+ if %ERRORLEVEL% == 0 goto :activate_venv_and_maybe_update
50
+
51
+ :: Venv does not exist, create it
52
+ echo Virtual environment not found in "%VENV_DIR%". Creating a new one.
53
+ for /f "delims=" %%i in ('CALL %PYTHON% -c "import sys; print(sys.executable)"') do set PYTHON_FULLNAME="%%i"
54
+ echo Creating venv in directory %VENV_DIR% using python %PYTHON_FULLNAME%
55
+ %PYTHON_FULLNAME% -m venv "%VENV_DIR%" >tmp/stdout.txt 2>tmp/stderr.txt
56
+ if %ERRORLEVEL% NEQ 0 (
57
+ echo Unable to create venv in directory "%VENV_DIR%"
58
+ goto :show_stdout_stderr
59
+ )
60
+ echo Venv created.
61
+
62
+ :: Install requirements for the first time if venv was just created
63
+ :: This section handles the initial installation of packages from requirements.txt
64
+ :: immediately after a new virtual environment is created.
65
+ echo Checking for requirements.txt for initial setup in %~dp0
66
+ if exist "%~dp0requirements.txt" (
67
+ echo Found requirements.txt, attempting to install for initial setup...
68
+ call "%VENV_DIR%\Scripts\activate.bat"
69
+ echo Installing packages from requirements.txt ^(initial setup^)...
70
+ "%VENV_DIR%\Scripts\python.exe" -m pip install -r "%~dp0requirements.txt"
71
+ if %ERRORLEVEL% NEQ 0 (
72
+ echo Failed to install requirements during initial setup. Please check the output above.
73
+ pause
74
+ goto :show_stdout_stderr_custom_pip_initial
75
+ )
76
+ echo Initial requirements installed successfully.
77
+ call "%VENV_DIR%\Scripts\deactivate.bat"
78
+ ) else (
79
+ echo No requirements.txt found for initial setup, skipping package installation.
80
+ )
81
+ goto :activate_venv_and_maybe_update
82
+
83
+
84
+ :activate_venv_and_maybe_update
85
+ :: This label is reached if the venv exists or was just created.
86
+ :: Set PYTHON to point to the venv's Python interpreter.
87
+ set PYTHON="%VENV_DIR%\Scripts\Python.exe"
88
+ echo Activating venv: %PYTHON%
89
+
90
+ :: Always update requirements if ALWAYS_UPDATE_REQS is 1
91
+ :: This section allows for updating packages from requirements.txt on every launch
92
+ :: if the ALWAYS_UPDATE_REQS variable is set to 1.
93
+ if defined ALWAYS_UPDATE_REQS (
94
+ if "%ALWAYS_UPDATE_REQS%"=="1" (
95
+ echo ALWAYS_UPDATE_REQS is enabled.
96
+ if exist "%~dp0requirements.txt" (
97
+ echo Attempting to update packages from requirements.txt...
98
+ REM No need to call activate.bat here again, PYTHON is already set to the venv's python
99
+ %PYTHON% -m pip install -r "%~dp0requirements.txt"
100
+ if %ERRORLEVEL% NEQ 0 (
101
+ echo Failed to update requirements. Please check the output above.
102
+ pause
103
+ goto :endofscript
104
+ )
105
+ echo Requirements updated successfully.
106
+ ) else (
107
+ echo ALWAYS_UPDATE_REQS is enabled, but no requirements.txt found. Skipping update.
108
+ )
109
+ ) else (
110
+ echo ALWAYS_UPDATE_REQS is not enabled or not set to 1. Skipping routine update.
111
+ )
112
+ )
113
+
114
+ goto :launch
115
+
116
+ :skip_venv_entirely
117
+ :: This label is reached if venv usage is explicitly skipped.
118
+ echo Skipping venv.
119
+ goto :launch
120
+
121
+ :launch
122
+ :: Launch the main application
123
+ echo Launching Web UI with arguments: %COMMANDLINE_ARGS% %*
124
+ %PYTHON% %APPLICATION_NAME% %COMMANDLINE_ARGS% %*
125
+ echo Launch finished.
126
+ pause
127
+ exit /b
128
+
129
+ :show_stdout_stderr_custom_pip_initial
130
+ :: Custom error handler for failures during the initial pip install process.
131
+ echo.
132
+ echo exit code ^(pip initial install^): %errorlevel%
133
+ echo Errors during initial pip install. See output above.
134
+ echo.
135
+ echo Launch unsuccessful. Exiting.
136
+ pause
137
+ exit /b
138
+
139
+
140
+ :show_stdout_stderr
141
+ :: General error handler: displays stdout and stderr from the tmp directory.
142
+ echo.
143
+ echo exit code: %errorlevel%
144
+
145
+ for /f %%i in ("tmp\stdout.txt") do set size=%%~zi
146
+ if %size% equ 0 goto :show_stderr
147
+ echo.
148
+ echo stdout:
149
+ type tmp\stdout.txt
150
+
151
+ :show_stderr
152
+ for /f %%i in ("tmp\stderr.txt") do set size=%%~zi
153
+ if %size% equ 0 goto :endofscript
154
+ echo.
155
+ echo stderr:
156
+ type tmp\stderr.txt
157
+
158
+ :endofscript
159
+ echo.
160
+ echo Launch unsuccessful. Exiting.
161
+ pause
162
+ exit /b