senmaonk commited on
Commit
e9d80e9
·
verified ·
1 Parent(s): b0b0cf4

Upload 4 files

Browse files
Files changed (4) hide show
  1. app.py +308 -154
  2. inference_InterLCM.py +346 -0
  3. sdxlturbo.py +154 -0
  4. sepia.py +18 -0
app.py CHANGED
@@ -1,154 +1,308 @@
1
- import gradio as gr
2
- import numpy as np
3
- import random
4
-
5
- import spaces #[uncomment to use ZeroGPU]
6
- from diffusers import DiffusionPipeline
7
- import torch
8
-
9
- device = "cuda" if torch.cuda.is_available() else "cpu"
10
- model_repo_id = "stabilityai/sdxl-turbo" # Replace to the model you would like to use
11
-
12
- if torch.cuda.is_available():
13
- torch_dtype = torch.float16
14
- else:
15
- torch_dtype = torch.float32
16
-
17
- pipe = DiffusionPipeline.from_pretrained(model_repo_id, torch_dtype=torch_dtype)
18
- pipe = pipe.to(device)
19
-
20
- MAX_SEED = np.iinfo(np.int32).max
21
- MAX_IMAGE_SIZE = 1024
22
-
23
-
24
- @spaces.GPU #[uncomment to use ZeroGPU]
25
- def infer(
26
- prompt,
27
- negative_prompt,
28
- seed,
29
- randomize_seed,
30
- width,
31
- height,
32
- guidance_scale,
33
- num_inference_steps,
34
- progress=gr.Progress(track_tqdm=True),
35
- ):
36
- if randomize_seed:
37
- seed = random.randint(0, MAX_SEED)
38
-
39
- generator = torch.Generator().manual_seed(seed)
40
-
41
- image = pipe(
42
- prompt=prompt,
43
- negative_prompt=negative_prompt,
44
- guidance_scale=guidance_scale,
45
- num_inference_steps=num_inference_steps,
46
- width=width,
47
- height=height,
48
- generator=generator,
49
- ).images[0]
50
-
51
- return image, seed
52
-
53
-
54
- examples = [
55
- "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k",
56
- "An astronaut riding a green horse",
57
- "A delicious ceviche cheesecake slice",
58
- ]
59
-
60
- css = """
61
- #col-container {
62
- margin: 0 auto;
63
- max-width: 640px;
64
- }
65
- """
66
-
67
- with gr.Blocks(css=css) as demo:
68
- with gr.Column(elem_id="col-container"):
69
- gr.Markdown(" # Text-to-Image Gradio Template")
70
-
71
- with gr.Row():
72
- prompt = gr.Text(
73
- label="Prompt",
74
- show_label=False,
75
- max_lines=1,
76
- placeholder="Enter your prompt",
77
- container=False,
78
- )
79
-
80
- run_button = gr.Button("Run", scale=0, variant="primary")
81
-
82
- result = gr.Image(label="Result", show_label=False)
83
-
84
- with gr.Accordion("Advanced Settings", open=False):
85
- negative_prompt = gr.Text(
86
- label="Negative prompt",
87
- max_lines=1,
88
- placeholder="Enter a negative prompt",
89
- visible=False,
90
- )
91
-
92
- seed = gr.Slider(
93
- label="Seed",
94
- minimum=0,
95
- maximum=MAX_SEED,
96
- step=1,
97
- value=0,
98
- )
99
-
100
- randomize_seed = gr.Checkbox(label="Randomize seed", value=True)
101
-
102
- with gr.Row():
103
- width = gr.Slider(
104
- label="Width",
105
- minimum=256,
106
- maximum=MAX_IMAGE_SIZE,
107
- step=32,
108
- value=1024, # Replace with defaults that work for your model
109
- )
110
-
111
- height = gr.Slider(
112
- label="Height",
113
- minimum=256,
114
- maximum=MAX_IMAGE_SIZE,
115
- step=32,
116
- value=1024, # Replace with defaults that work for your model
117
- )
118
-
119
- with gr.Row():
120
- guidance_scale = gr.Slider(
121
- label="Guidance scale",
122
- minimum=0.0,
123
- maximum=10.0,
124
- step=0.1,
125
- value=0.0, # Replace with defaults that work for your model
126
- )
127
-
128
- num_inference_steps = gr.Slider(
129
- label="Number of inference steps",
130
- minimum=1,
131
- maximum=50,
132
- step=1,
133
- value=2, # Replace with defaults that work for your model
134
- )
135
-
136
- gr.Examples(examples=examples, inputs=[prompt])
137
- gr.on(
138
- triggers=[run_button.click, prompt.submit],
139
- fn=infer,
140
- inputs=[
141
- prompt,
142
- negative_prompt,
143
- seed,
144
- randomize_seed,
145
- width,
146
- height,
147
- guidance_scale,
148
- num_inference_steps,
149
- ],
150
- outputs=[result, seed],
151
- )
152
-
153
- if __name__ == "__main__":
154
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import numpy as np
3
+ import random
4
+
5
+ import spaces #[uncomment to use ZeroGPU]
6
+ from diffusers import DiffusionPipeline
7
+ import torch
8
+
9
+ device = "cuda" if torch.cuda.is_available() else "cpu"
10
+ # model_repo_id = "/data/stabilityai/sdxl-turbo" # Replace to the model you would like to use
11
+ #
12
+ # if torch.cuda.is_available():
13
+ # torch_dtype = torch.float16
14
+ # else:
15
+ # torch_dtype = torch.float32
16
+ #
17
+ # pipe = DiffusionPipeline.from_pretrained(model_repo_id, torch_dtype=torch_dtype)
18
+ # pipe = pipe.to(device)
19
+
20
+ # ------------------ set up InterLCM restorer ------------------- #
21
+ import os
22
+ import cv2
23
+ import argparse
24
+ import glob
25
+ import re
26
+ import torch
27
+ from torchvision.transforms.functional import normalize
28
+ from basicsr.utils import imwrite, img2tensor, tensor2img
29
+ from basicsr.utils.download_util import load_file_from_url
30
+ from basicsr.utils.misc import gpu_is_available, get_device
31
+ from facelib.utils.face_restoration_helper import FaceRestoreHelper
32
+ from facelib.utils.misc import is_gray
33
+
34
+ from basicsr.utils.registry import ARCH_REGISTRY
35
+
36
+ # CILP
37
+ import clip
38
+ import torchvision.transforms as transforms
39
+
40
+ from basicsr.utils.clip_util import VisionTransformer
41
+ clip.model.VisionTransformer = VisionTransformer
42
+
43
+ # LCM
44
+ from diffusers import DiffusionPipeline, UNet2DConditionModel, ControlNetModel
45
+ from basicsr.utils.lcm_utils import register_lcm_forward, register_lcmschedule_step
46
+ from basicsr.archs.rrdbnet_arch import RRDBNet
47
+ from basicsr.utils.realesrgan_utils import RealESRGANer
48
+
49
+ from scripts.wavelet_color_fix import wavelet_reconstruction, adaptive_instance_normalization
50
+
51
+ visual_encoder_path = "weights/InterLCM/visual_encoder_3step.pth"
52
+ spatial_encoder_path = "weights/InterLCM/spatial_encoder_3step.pth"
53
+ visual_encoder_path_1step = "weights/InterLCM/visual_encoder_1step.pth"
54
+ spatial_encoder_path_1step = "weights/InterLCM/spatial_encoder_1step.pth"
55
+ sd_path = "stable-diffusion-v1-5/stable-diffusion-v1-5"
56
+ lcm_path = "SimianLuo/LCM_Dreamshaper_v7"
57
+ detection_model = "retinaface_resnet50"
58
+
59
+ # CLIPImageEncoder
60
+ clip_model, clip_preprocess = clip.load('ViT-B/16', device=device)
61
+ preprocess = transforms.Compose([transforms.Normalize(mean=[-1.0, -1.0, -1.0], std=[2.0, 2.0,
62
+ 2.0])] + # Un-normalize from [-1.0, 1.0] (GAN output) to [0, 1].
63
+ clip_preprocess.transforms[:2] + # to match CLIP input scale assumptions
64
+ clip_preprocess.transforms[4:]) # + skip convert PIL to tensor
65
+
66
+ # Visual Encoder
67
+ visual_encoder = ARCH_REGISTRY.get('VisualEncoder')(nf=64, emb_dim=197, ch_mult=[2, 4, 8], res_blocks=2,
68
+ img_size=512).to(device)
69
+ checkpoint_ve = torch.load(visual_encoder_path)['params_ema']
70
+ visual_encoder.load_state_dict(checkpoint_ve)
71
+ visual_encoder.eval()
72
+ del checkpoint_ve
73
+
74
+ # Spatial Encoder
75
+ unet = UNet2DConditionModel.from_pretrained(pretrained_model_name_or_path=sd_path, subfolder="unet")
76
+ spatial_encoder = ControlNetModel.from_unet(unet).to(device)
77
+ checkpoint_c = torch.load(spatial_encoder_path)['params_ema']
78
+ spatial_encoder.load_state_dict(checkpoint_c)
79
+ spatial_encoder.eval()
80
+ del unet
81
+
82
+ # Visual Encoder 1-step
83
+ visual_encoder_1step = ARCH_REGISTRY.get('VisualEncoder')(nf=64, emb_dim=197, ch_mult=[2, 4, 8], res_blocks=2,
84
+ img_size=512).to(device)
85
+ checkpoint_ve = torch.load(visual_encoder_path_1step)['params_ema']
86
+ visual_encoder_1step.load_state_dict(checkpoint_ve)
87
+ visual_encoder_1step.eval()
88
+ del checkpoint_ve
89
+
90
+ # Spatial Encoder
91
+ unet = UNet2DConditionModel.from_pretrained(pretrained_model_name_or_path=sd_path, subfolder="unet")
92
+ spatial_encoder_1step = ControlNetModel.from_unet(unet).to(device)
93
+ checkpoint_c = torch.load(spatial_encoder_path_1step)['params_ema']
94
+ spatial_encoder_1step.load_state_dict(checkpoint_c)
95
+ spatial_encoder_1step.eval()
96
+ del unet
97
+
98
+ torch.cuda.empty_cache()
99
+
100
+ # lcm
101
+ lcm = DiffusionPipeline.from_pretrained(pretrained_model_name_or_path=lcm_path).to(device)
102
+
103
+ # set enhancer with RealESRGAN
104
+ def set_realesrgan():
105
+ half = True if torch.cuda.is_available() else False
106
+ model = RRDBNet(
107
+ num_in_ch=3,
108
+ num_out_ch=3,
109
+ num_feat=64,
110
+ num_block=23,
111
+ num_grow_ch=32,
112
+ scale=2,
113
+ )
114
+ upsampler = RealESRGANer(
115
+ scale=2,
116
+ model_path="weights/realesrgan/RealESRGAN_x2plus.pth",
117
+ model=model,
118
+ tile=400,
119
+ tile_pad=40,
120
+ pre_pad=0,
121
+ half=half,
122
+ device=device
123
+ )
124
+ return upsampler
125
+
126
+ upsampler = set_realesrgan()
127
+
128
+ upscale = 2
129
+ face_helper = FaceRestoreHelper(
130
+ upscale_factor=upscale,
131
+ face_size=512,
132
+ crop_ratio=(1, 1),
133
+ det_model=detection_model,
134
+ save_ext='png',
135
+ use_parse=True,
136
+ device=device)
137
+
138
+ # ------------------ set up InterLCM restorer ------------------- #
139
+ @spaces.GPU
140
+ def inference(input_img, interlcm_step, face_align, background_enhance, face_upsample):
141
+ # try:
142
+ only_center_face = False
143
+ draw_box = False
144
+
145
+ interlcm_step = int(interlcm_step)
146
+ assert interlcm_step in (1, 3)
147
+ if interlcm_step == 1:
148
+ register_lcm_forward(lcm, spatial_encoder_1step)
149
+ elif interlcm_step == 3:
150
+ register_lcm_forward(lcm, spatial_encoder)
151
+ register_lcmschedule_step(lcm.scheduler)
152
+
153
+ face_align = face_align if face_align is not None else True
154
+ has_aligned = not face_align
155
+
156
+ background_enhance = background_enhance if background_enhance is not None else True
157
+ bg_upsampler = upsampler if background_enhance else None
158
+
159
+ face_upsampler = upsampler if face_upsample else None
160
+
161
+ img = cv2.imread(str(input_img), cv2.IMREAD_COLOR)
162
+ print('\timage size:', img.shape)
163
+
164
+ face_helper.clean_all()
165
+ if has_aligned:
166
+ # the input faces are already cropped and aligned
167
+ img = cv2.resize(img, (512, 512), interpolation=cv2.INTER_LINEAR)
168
+ face_helper.is_gray = is_gray(img, threshold=10)
169
+ if face_helper.is_gray:
170
+ print('Grayscale input: True')
171
+ face_helper.cropped_faces = [img]
172
+ else:
173
+ face_helper.read_image(img)
174
+ # get face landmarks for each face
175
+ num_det_faces = face_helper.get_face_landmarks_5(
176
+ only_center_face=only_center_face, resize=640, eye_dist_threshold=5, device=device)
177
+ print(f'\tdetect {num_det_faces} faces')
178
+ # align and warp each face
179
+ face_helper.align_warp_face()
180
+
181
+ # face restoration for each cropped face
182
+ for idx, cropped_face in enumerate(face_helper.cropped_faces):
183
+ # prepare data
184
+ cropped_face_t = img2tensor(cropped_face / 255., bgr2rgb=True, float32=True)
185
+ normalize(cropped_face_t, (0.5, 0.5, 0.5), (0.5, 0.5, 0.5), inplace=True)
186
+ cropped_face_t = cropped_face_t.unsqueeze(0).to(device)
187
+
188
+ try:
189
+ with torch.no_grad():
190
+ input = preprocess(cropped_face_t)
191
+ img_emb = clip_model.encode_image(input)
192
+ img_emb = img_emb.to(torch.float)
193
+
194
+ if interlcm_step == 1:
195
+ visual_feat = visual_encoder_1step(img_emb)
196
+ elif interlcm_step == 3:
197
+ visual_feat = visual_encoder(img_emb)
198
+
199
+ latent_code = lcm.vae.encode(cropped_face_t)['latent_dist'].mean
200
+ latent_code = latent_code * 0.18215
201
+ output = lcm.forward(height=512, width=512, num_inference_steps=interlcm_step + 1,
202
+ guidance_scale=8.0, latents=latent_code,
203
+ prompt_embeds=visual_feat, output_type="pil", lcm_origin_steps=50,
204
+ lq_input=cropped_face_t).images
205
+ output = wavelet_reconstruction(output, cropped_face_t)
206
+ restored_face = tensor2img(output, rgb2bgr=True, min_max=(-1, 1))
207
+
208
+ del output
209
+ torch.cuda.empty_cache()
210
+ except Exception as error:
211
+ print(f'\tFailed inference for CodeFormer: {error}')
212
+ restored_face = tensor2img(cropped_face_t, rgb2bgr=True, min_max=(-1, 1))
213
+
214
+ restored_face = restored_face.astype('uint8')
215
+ face_helper.add_restored_face(restored_face, cropped_face)
216
+
217
+ # paste_back
218
+ if not has_aligned:
219
+ # upsample the background
220
+ if bg_upsampler is not None:
221
+ # Now only support RealESRGAN for upsampling background
222
+ bg_img = bg_upsampler.enhance(img, outscale=upscale)[0]
223
+ else:
224
+ bg_img = None
225
+ face_helper.get_inverse_affine(None)
226
+ # paste each restored face to the input image
227
+ if face_upsample and face_upsampler is not None:
228
+ restored_img = face_helper.paste_faces_to_input_image(upsample_img=bg_img, draw_box=draw_box,
229
+ face_upsampler=face_upsampler)
230
+ else:
231
+ restored_img = face_helper.paste_faces_to_input_image(upsample_img=bg_img, draw_box=draw_box)
232
+ else:
233
+ restored_img = restored_face
234
+
235
+ # save restored img
236
+ save_path = f'output/out.png'
237
+ imwrite(restored_img, save_path)
238
+
239
+ restored_img = cv2.cvtColor(restored_img, cv2.COLOR_BGR2RGB)
240
+
241
+ return restored_img
242
+ # except Exception as error:
243
+ # print('Global exception', error)
244
+ # return None
245
+
246
+
247
+ title = "InterLCM: Low-Quality Images as Intermediate States of Latent Consistency Models for Effective Blind Face Restoration"
248
+
249
+ description = r"""<center><img src='https://raw.githubusercontent.com/sen-mao/InterLCM/refs/heads/master/assets/interlcm_logo.jpg' alt='InterLCM logo' width="120"></center>
250
+ <br>
251
+ <b>Official Gradio demo</b> for <a href='https://github.com/sen-mao/InterLCM' target='_blank'><b>Low-Quality Images as Intermediate States of Latent Consistency Models for Effective Blind Face Restoration (ICLR 2025)</b></a><br>
252
+ 🔥 InterLCM is a robust blind face restoration algorithm.<br>
253
+ ⭐ If InterLCM is helpful to your images or projects, please help star this repo. Thanks! 🤗 <br>
254
+ """
255
+
256
+ article = r"""
257
+ If InterLCM is helpful, please help to ⭐ the <a href='https://github.com/sen-mao/InterLCM' target='_blank'>Github Repo</a>. Thanks!
258
+ [![GitHub Stars](https://img.shields.io/github/stars/sen-mao/InterLCM?style=social)](https://github.com/sen-mao/InterLCM)
259
+
260
+ ---
261
+
262
+ 📝 **Citation**
263
+ If our work is useful for your research, please consider citing:
264
+ ```bibtex
265
+ @inproceedings{li2025interlcm,
266
+ title={InterLCM: Low-Quality Images as Intermediate States of Latent Consistency Models for Effective Blind Face Restoration},
267
+ author={Li, Senmao and Wang, Kai and van de Weijer, Joost and Khan, Fahad Shahbaz and Guo, Chun-Le and Yang, Shiqi and Wang, Yaxing and Yang, Jian and Cheng, Ming-Ming},
268
+ booktitle={ICLR},
269
+ year={2025}
270
+ }
271
+ ```
272
+
273
+ 📧 **Contact**
274
+ If you have any questions, please feel free to reach me out at <b>senmaonk@gmail.com</b>.
275
+
276
+ <center><img src='https://visitor-badge.laobi.icu/badge?page_id=sen-mao/InterLCM&ltext=Visitors' alt='visitors'></center>
277
+ """
278
+
279
+
280
+ demo = gr.Interface(
281
+ inference, [
282
+ gr.Image(type="filepath", label="Input"),
283
+ gr.Radio(choices=["1", "3"], value="3", label="Select InterLCM step (InterLCM enables 1-step⚡ BFR under non-extreme degradation conditions)"),
284
+ gr.Checkbox(value=True, label="Pre_Face_Align"),
285
+ gr.Checkbox(value=True, label="Background_Enhance"),
286
+ gr.Checkbox(value=True, label="Face_Upsample"),
287
+ ], [
288
+ gr.Image(type="numpy", label="Output")
289
+ ],
290
+ title=title,
291
+ description=description,
292
+ article=article,
293
+ examples=[
294
+ ['inputs/cropped_faces/0631.png', "3", False, False, False],
295
+ ['inputs/cropped_faces/Nora_Bendijo_0001_00.png', "3", False, False, False],
296
+ ['inputs/whole_imgs/03.jpg', "1", True, True, True],
297
+ ['inputs/whole_imgs/04.jpg', "3", True, True, True],
298
+ ['inputs/whole_imgs/05.jpg', "3", True, True, True]
299
+ ],
300
+ concurrency_limit=2,
301
+ allow_flagging="never",
302
+ )
303
+
304
+
305
+ if __name__ == "__main__":
306
+ # DEBUG = os.getenv('DEBUG') == '1'
307
+ # demo.launch(server_name="0.0.0.0", server_port=7861, max_threads=10, share=False)
308
+ demo.launch()
inference_InterLCM.py ADDED
@@ -0,0 +1,346 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import cv2
3
+ import argparse
4
+ import glob
5
+ import re
6
+ import torch
7
+ from torchvision.transforms.functional import normalize
8
+ from basicsr.utils import imwrite, img2tensor, tensor2img
9
+ from basicsr.utils.download_util import load_file_from_url
10
+ from basicsr.utils.misc import gpu_is_available, get_device
11
+ from facelib.utils.face_restoration_helper import FaceRestoreHelper
12
+ from facelib.utils.misc import is_gray
13
+
14
+ from basicsr.utils.registry import ARCH_REGISTRY
15
+
16
+ # CILP
17
+ import clip
18
+ import torchvision.transforms as transforms
19
+
20
+ from basicsr.utils.clip_util import VisionTransformer
21
+ clip.model.VisionTransformer = VisionTransformer
22
+
23
+ # LCM
24
+ from diffusers import DiffusionPipeline, UNet2DConditionModel, ControlNetModel
25
+ from basicsr.utils.lcm_utils import register_lcm_forward, register_lcmschedule_step
26
+
27
+ from scripts.wavelet_color_fix import wavelet_reconstruction, adaptive_instance_normalization
28
+
29
+ def set_realesrgan(args):
30
+ from basicsr.archs.rrdbnet_arch import RRDBNet
31
+ from basicsr.utils.realesrgan_utils import RealESRGANer
32
+
33
+ use_half = False
34
+ if torch.cuda.is_available(): # set False in CPU/MPS mode
35
+ no_half_gpu_list = ['1650', '1660'] # set False for GPUs that don't support f16
36
+ if not True in [gpu in torch.cuda.get_device_name(0) for gpu in no_half_gpu_list]:
37
+ use_half = True
38
+
39
+ model = RRDBNet(
40
+ num_in_ch=3,
41
+ num_out_ch=3,
42
+ num_feat=64,
43
+ num_block=23,
44
+ num_grow_ch=32,
45
+ scale=2,
46
+ )
47
+ upsampler = RealESRGANer(
48
+ scale=2,
49
+ model_path="https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/RealESRGAN_x2plus.pth",
50
+ model=model,
51
+ tile=args.bg_tile,
52
+ tile_pad=40,
53
+ pre_pad=0,
54
+ half=use_half
55
+ )
56
+
57
+ if not gpu_is_available(): # CPU
58
+ import warnings
59
+ warnings.warn('Running on CPU now! Make sure your PyTorch version matches your CUDA.'
60
+ 'The unoptimized RealESRGAN is slow on CPU. '
61
+ 'If you want to disable it, please remove `--bg_upsampler` and `--face_upsample` in command.',
62
+ category=RuntimeWarning)
63
+ return upsampler
64
+
65
+ @torch.no_grad()
66
+ def main():
67
+ device = get_device()
68
+ parser = argparse.ArgumentParser()
69
+
70
+ parser.add_argument('-i', '--input_path', type=str, default='./inputs/whole_imgs',
71
+ help='Input image, video or folder. Default: inputs/whole_imgs')
72
+ parser.add_argument('-o', '--output_path', type=str, default="results",
73
+ help='Output folder. Default: results/<input_name>')
74
+ parser.add_argument('-s', '--upscale', type=int, default=2,
75
+ help='The final upsampling scale of the image. Default: 2')
76
+ parser.add_argument('--has_aligned', action='store_true', help='Input are cropped and aligned faces. Default: False')
77
+ parser.add_argument('--only_center_face', action='store_true', help='Only restore the center face. Default: False')
78
+ parser.add_argument('--draw_box', action='store_true', help='Draw the bounding box for the detected faces. Default: False')
79
+ # large det_model: 'YOLOv5l', 'retinaface_resnet50'
80
+ # small det_model: 'YOLOv5n', 'retinaface_mobile0.25'
81
+ parser.add_argument('--detection_model', type=str, default='retinaface_resnet50',
82
+ help='Face detector. Optional: retinaface_resnet50, retinaface_mobile0.25, YOLOv5l, YOLOv5n, dlib. \
83
+ Default: retinaface_resnet50')
84
+ parser.add_argument('--bg_upsampler', type=str, default='None', help='Background upsampler. Optional: realesrgan')
85
+ parser.add_argument('--face_upsample', action='store_true', help='Face upsampler after enhancement. Default: False')
86
+ parser.add_argument('--bg_tile', type=int, default=400, help='Tile size for background sampler. Default: 400')
87
+ parser.add_argument('--suffix', type=str, default=None, help='Suffix of the restored faces. Default: None')
88
+ parser.add_argument('--save_video_fps', type=float, default=None, help='Frame rate for saving video. Default: None')
89
+ # LCM
90
+ parser.add_argument('--num_inference_steps', type=int, default=4, help='T for lcm')
91
+ parser.add_argument('--visual_encoder_path', type=str,
92
+ default='weights/InterLCM/visual_encoder_3step.pth',
93
+ help='visual_encoder checkpoint')
94
+ parser.add_argument('--spatial_encoder_path', type=str,
95
+ default='weights/InterLCM/spatial_encoder_3step.pth',
96
+ help='spatial_encoder checkpoint')
97
+ parser.add_argument('--sd_path', type=str,
98
+ default='/data/runwayml/stable-diffusion-v1-5',
99
+ help='sd pre-trined model')
100
+ parser.add_argument('--lcm_path', type=str,
101
+ default='/data/SimianLuo/LCM_Dreamshaper_v7',
102
+ help='lcm pre-trined model')
103
+
104
+ parser.add_argument(
105
+ "--colorfix_type",
106
+ type=str,
107
+ default="wavelet",
108
+ help="Color fix type to adjust the color of reconstructed HR result according to LR input: "
109
+ "adain; wavelet (used in paper); nofix",
110
+ )
111
+
112
+ args = parser.parse_args()
113
+ print(args)
114
+
115
+ interlcm_step = int(re.findall(r'\d+', args.visual_encoder_path)[0])
116
+ args.output_path = args.output_path.replace(args.output_path.split('/')[0],
117
+ f"{args.output_path.split('/')[0]}[{args.colorfix_type}]/interlcm_{interlcm_step}step")
118
+
119
+ assert args.num_inference_steps - 1 == interlcm_step
120
+
121
+ # ------------------------ input & output ------------------------
122
+ input_video = False
123
+ if args.input_path.endswith(('jpg', 'jpeg', 'png', 'JPG', 'JPEG', 'PNG')): # input single img path
124
+ input_img_list = [args.input_path]
125
+ result_root = f'results/test_img'
126
+ elif args.input_path.endswith(('mp4', 'mov', 'avi', 'MP4', 'MOV', 'AVI')): # input video path
127
+ from basicsr.utils.video_util import VideoReader, VideoWriter
128
+ input_img_list = []
129
+ vidreader = VideoReader(args.input_path)
130
+ image = vidreader.get_frame()
131
+ while image is not None:
132
+ input_img_list.append(image)
133
+ image = vidreader.get_frame()
134
+ audio = vidreader.get_audio()
135
+ fps = vidreader.get_fps() if args.save_video_fps is None else args.save_video_fps
136
+ video_name = os.path.basename(args.input_path)[:-4]
137
+ result_root = f'results/{video_name}'
138
+ input_video = True
139
+ vidreader.close()
140
+ else: # input img folder
141
+ if args.input_path.endswith('/'): # solve when path ends with /
142
+ args.input_path = args.input_path[:-1]
143
+ # scan all the jpg and png images
144
+ input_img_list = sorted(glob.glob(os.path.join(args.input_path, '*.[jpJP][pnPN]*[gG]')))
145
+ result_root = f'results/{os.path.basename(args.input_path)}'
146
+
147
+ if not args.output_path is None: # set output path
148
+ result_root = args.output_path
149
+
150
+ test_img_num = len(input_img_list)
151
+ if test_img_num == 0:
152
+ raise FileNotFoundError('No input image/video is found...\n'
153
+ '\tNote that --input_path for video should end with .mp4|.mov|.avi')
154
+
155
+ # ------------------ set up background upsampler ------------------
156
+ if args.bg_upsampler == 'realesrgan':
157
+ bg_upsampler = set_realesrgan(args)
158
+ else:
159
+ bg_upsampler = None
160
+
161
+ # ------------------ set up face upsampler ------------------
162
+ if args.face_upsample:
163
+ if bg_upsampler is not None:
164
+ face_upsampler = bg_upsampler
165
+ else:
166
+ face_upsampler = set_realesrgan(args)
167
+ else:
168
+ face_upsampler = None
169
+
170
+ # ------------------ set up InterLCM restorer -------------------
171
+
172
+ # CLIPImageEncoder
173
+ clip_model, clip_preprocess = clip.load('ViT-B/16', device=device)
174
+ preprocess = transforms.Compose([transforms.Normalize(mean=[-1.0, -1.0, -1.0], std=[2.0, 2.0, 2.0])] + # Un-normalize from [-1.0, 1.0] (GAN output) to [0, 1].
175
+ clip_preprocess.transforms[:2] + # to match CLIP input scale assumptions
176
+ clip_preprocess.transforms[4:]) # + skip convert PIL to tensor
177
+
178
+ # Visual Encoder
179
+ visual_encoder = ARCH_REGISTRY.get('VisualEncoder')(nf=64, emb_dim=197, ch_mult=[2,4,8], res_blocks=2, img_size=512).to(device)
180
+ checkpoint_ve = torch.load(args.visual_encoder_path)['params_ema']
181
+ visual_encoder.load_state_dict(checkpoint_ve)
182
+ visual_encoder.eval()
183
+
184
+ # Spatial Encoder
185
+ unet = UNet2DConditionModel.from_pretrained(pretrained_model_name_or_path=args.sd_path, subfolder="unet")
186
+ spatial_encoder = ControlNetModel.from_unet(unet).to(device)
187
+ checkpoint_c = torch.load(args.spatial_encoder_path)['params_ema']
188
+ spatial_encoder.load_state_dict(checkpoint_c)
189
+ spatial_encoder.eval()
190
+
191
+ # lcm
192
+ lcm = DiffusionPipeline.from_pretrained(pretrained_model_name_or_path=args.lcm_path).to(device)
193
+
194
+ register_lcm_forward(lcm, spatial_encoder)
195
+ register_lcmschedule_step(lcm.scheduler)
196
+
197
+
198
+ # ------------------ set up FaceRestoreHelper -------------------
199
+ # large det_model: 'YOLOv5l', 'retinaface_resnet50'
200
+ # small det_model: 'YOLOv5n', 'retinaface_mobile0.25'
201
+ if not args.has_aligned:
202
+ print(f'Face detection model: {args.detection_model}')
203
+ if bg_upsampler is not None:
204
+ print(f'Background upsampling: True, Face upsampling: {args.face_upsample}')
205
+ else:
206
+ print(f'Background upsampling: False, Face upsampling: {args.face_upsample}')
207
+
208
+ face_helper = FaceRestoreHelper(
209
+ args.upscale,
210
+ face_size=512,
211
+ crop_ratio=(1, 1),
212
+ det_model = args.detection_model,
213
+ save_ext='png',
214
+ use_parse=True,
215
+ device=device)
216
+
217
+ # -------------------- start to processing ---------------------
218
+ for i, img_path in enumerate(input_img_list):
219
+ # clean all the intermediate results to process the next image
220
+ face_helper.clean_all()
221
+
222
+ if isinstance(img_path, str):
223
+ img_name = os.path.basename(img_path)
224
+ basename, ext = os.path.splitext(img_name)
225
+ print(f'[{i+1}/{test_img_num}] Processing: {img_name}')
226
+ img = cv2.imread(img_path, cv2.IMREAD_COLOR)
227
+ else: # for video processing
228
+ basename = str(i).zfill(6)
229
+ img_name = f'{video_name}_{basename}' if input_video else basename
230
+ print(f'[{i+1}/{test_img_num}] Processing: {img_name}')
231
+ img = img_path
232
+
233
+ if args.has_aligned:
234
+ # the input faces are already cropped and aligned
235
+ img = cv2.resize(img, (512, 512), interpolation=cv2.INTER_LINEAR)
236
+ face_helper.is_gray = is_gray(img, threshold=10)
237
+ if face_helper.is_gray:
238
+ print('Grayscale input: True')
239
+ face_helper.cropped_faces = [img]
240
+ else:
241
+ face_helper.read_image(img)
242
+ # get face landmarks for each face
243
+ num_det_faces = face_helper.get_face_landmarks_5(
244
+ only_center_face=args.only_center_face, resize=640, eye_dist_threshold=5)
245
+ print(f'\tdetect {num_det_faces} faces')
246
+ # align and warp each face
247
+ face_helper.align_warp_face()
248
+
249
+ # face restoration for each cropped face
250
+ for idx, cropped_face in enumerate(face_helper.cropped_faces):
251
+ # prepare data
252
+ cropped_face_t = img2tensor(cropped_face / 255., bgr2rgb=True, float32=True)
253
+ normalize(cropped_face_t, (0.5, 0.5, 0.5), (0.5, 0.5, 0.5), inplace=True)
254
+ cropped_face_t = cropped_face_t.unsqueeze(0).to(device)
255
+
256
+ try:
257
+ with torch.no_grad():
258
+ input = preprocess(cropped_face_t)
259
+ img_emb = clip_model.encode_image(input)
260
+ img_emb = img_emb.to(torch.float)
261
+
262
+ visual_feat = visual_encoder(img_emb)
263
+
264
+ latent_code = lcm.vae.encode(cropped_face_t)['latent_dist'].mean
265
+ latent_code = latent_code * 0.18215
266
+ output = lcm.forward(height=512, width=512, num_inference_steps=args.num_inference_steps, guidance_scale=8.0, latents=latent_code,
267
+ prompt_embeds=visual_feat, output_type="pil", lcm_origin_steps=50, lq_input=cropped_face_t).images
268
+
269
+ # colorfix from StableSR
270
+ if args.colorfix_type == 'adain':
271
+ output = adaptive_instance_normalization(output, cropped_face_t)
272
+ elif args.colorfix_type == 'wavelet':
273
+ output = wavelet_reconstruction(output, cropped_face_t)
274
+
275
+ restored_face = tensor2img(output, rgb2bgr=True, min_max=(-1, 1))
276
+ del output
277
+ torch.cuda.empty_cache()
278
+ except Exception as error:
279
+ print(f'\tFailed inference for CodeFormer: {error}')
280
+ restored_face = tensor2img(cropped_face_t, rgb2bgr=True, min_max=(-1, 1))
281
+
282
+ restored_face = restored_face.astype('uint8')
283
+ face_helper.add_restored_face(restored_face, cropped_face)
284
+
285
+ # paste_back
286
+ if not args.has_aligned:
287
+ # upsample the background
288
+ if bg_upsampler is not None:
289
+ # Now only support RealESRGAN for upsampling background
290
+ bg_img = bg_upsampler.enhance(img, outscale=args.upscale)[0]
291
+ else:
292
+ bg_img = None
293
+ face_helper.get_inverse_affine(None)
294
+ # paste each restored face to the input image
295
+ if args.face_upsample and face_upsampler is not None:
296
+ restored_img = face_helper.paste_faces_to_input_image(upsample_img=bg_img, draw_box=args.draw_box, face_upsampler=face_upsampler)
297
+ else:
298
+ restored_img = face_helper.paste_faces_to_input_image(upsample_img=bg_img, draw_box=args.draw_box)
299
+
300
+ # save faces
301
+ for idx, (cropped_face, restored_face) in enumerate(zip(face_helper.cropped_faces, face_helper.restored_faces)):
302
+ # save cropped face
303
+ if not args.has_aligned:
304
+ save_crop_path = os.path.join(result_root, 'cropped_faces', f'{basename}_{idx:02d}.png')
305
+ imwrite(cropped_face, save_crop_path)
306
+ # save restored face
307
+ if args.has_aligned:
308
+ save_face_name = f'{basename}.png'
309
+ else:
310
+ save_face_name = f'{basename}_{idx:02d}.png'
311
+ if args.suffix is not None:
312
+ save_face_name = f'{save_face_name[:-4]}_{args.suffix}.png'
313
+ save_restore_path = os.path.join(result_root, 'restored_faces', save_face_name)
314
+ imwrite(restored_face, save_restore_path)
315
+
316
+ # save restored img
317
+ if not args.has_aligned and restored_img is not None:
318
+ if args.suffix is not None:
319
+ basename = f'{basename}_{args.suffix}'
320
+ save_restore_path = os.path.join(result_root, 'final_results', f'{basename}.png')
321
+ imwrite(restored_img, save_restore_path)
322
+
323
+ # save enhanced video
324
+ if input_video:
325
+ print('Video Saving...')
326
+ # load images
327
+ video_frames = []
328
+ img_list = sorted(glob.glob(os.path.join(result_root, 'final_results', '*.[jp][pn]g')))
329
+ for img_path in img_list:
330
+ img = cv2.imread(img_path)
331
+ video_frames.append(img)
332
+ # write images to video
333
+ height, width = video_frames[0].shape[:2]
334
+ if args.suffix is not None:
335
+ video_name = f'{video_name}_{args.suffix}.png'
336
+ save_restore_path = os.path.join(result_root, f'{video_name}.mp4')
337
+ vidwriter = VideoWriter(save_restore_path, height, width, fps, audio)
338
+
339
+ for f in video_frames:
340
+ vidwriter.write_frame(f)
341
+ vidwriter.close()
342
+
343
+ print(f'\nAll results are saved in {result_root}')
344
+
345
+ if __name__ == '__main__':
346
+ main()
sdxlturbo.py ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import numpy as np
3
+ import random
4
+
5
+ # import spaces #[uncomment to use ZeroGPU]
6
+ from diffusers import DiffusionPipeline
7
+ import torch
8
+
9
+ device = "cuda" if torch.cuda.is_available() else "cpu"
10
+ model_repo_id = "/data/stabilityai/sdxl-turbo" # Replace to the model you would like to use
11
+
12
+ if torch.cuda.is_available():
13
+ torch_dtype = torch.float16
14
+ else:
15
+ torch_dtype = torch.float32
16
+
17
+ pipe = DiffusionPipeline.from_pretrained(model_repo_id, torch_dtype=torch_dtype)
18
+ pipe = pipe.to(device)
19
+
20
+ MAX_SEED = np.iinfo(np.int32).max
21
+ MAX_IMAGE_SIZE = 512
22
+
23
+
24
+ # @spaces.GPU #[uncomment to use ZeroGPU]
25
+ def infer(
26
+ prompt,
27
+ negative_prompt,
28
+ seed,
29
+ randomize_seed,
30
+ width,
31
+ height,
32
+ guidance_scale,
33
+ num_inference_steps,
34
+ progress=gr.Progress(track_tqdm=True),
35
+ ):
36
+ if randomize_seed:
37
+ seed = random.randint(0, MAX_SEED)
38
+
39
+ generator = torch.Generator().manual_seed(seed)
40
+
41
+ image = pipe(
42
+ prompt=prompt,
43
+ negative_prompt=negative_prompt,
44
+ guidance_scale=guidance_scale,
45
+ num_inference_steps=num_inference_steps,
46
+ width=width,
47
+ height=height,
48
+ generator=generator,
49
+ ).images[0]
50
+
51
+ return image, seed
52
+
53
+
54
+ examples = [
55
+ "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k",
56
+ "An astronaut riding a green horse",
57
+ "A delicious ceviche cheesecake slice",
58
+ ]
59
+
60
+ css = """
61
+ #col-container {
62
+ margin: 0 auto;
63
+ max-width: 640px;
64
+ }
65
+ """
66
+
67
+ with gr.Blocks(css=css) as demo:
68
+ with gr.Column(elem_id="col-container"):
69
+ gr.Markdown(" # InterLCM: Low-Quality Images as Intermediate States of Latent Consistency Models for Effective Blind Face Restoration")
70
+
71
+ with gr.Row():
72
+ prompt = gr.Text(
73
+ label="Prompt",
74
+ show_label=False,
75
+ max_lines=1,
76
+ placeholder="Enter your prompt",
77
+ container=False,
78
+ )
79
+
80
+ run_button = gr.Button("Run", scale=0, variant="primary")
81
+
82
+ result = gr.Image(label="Result", show_label=False)
83
+
84
+ with gr.Accordion("Advanced Settings", open=False):
85
+ negative_prompt = gr.Text(
86
+ label="Negative prompt",
87
+ max_lines=1,
88
+ placeholder="Enter a negative prompt",
89
+ visible=False,
90
+ )
91
+
92
+ seed = gr.Slider(
93
+ label="Seed",
94
+ minimum=0,
95
+ maximum=MAX_SEED,
96
+ step=1,
97
+ value=0,
98
+ )
99
+
100
+ randomize_seed = gr.Checkbox(label="Randomize seed", value=True)
101
+
102
+ with gr.Row():
103
+ width = gr.Slider(
104
+ label="Width",
105
+ minimum=256,
106
+ maximum=MAX_IMAGE_SIZE,
107
+ step=32,
108
+ value=1024, # Replace with defaults that work for your model
109
+ )
110
+
111
+ height = gr.Slider(
112
+ label="Height",
113
+ minimum=256,
114
+ maximum=MAX_IMAGE_SIZE,
115
+ step=32,
116
+ value=1024, # Replace with defaults that work for your model
117
+ )
118
+
119
+ with gr.Row():
120
+ guidance_scale = gr.Slider(
121
+ label="Guidance scale",
122
+ minimum=0.0,
123
+ maximum=10.0,
124
+ step=0.1,
125
+ value=0.0, # Replace with defaults that work for your model
126
+ )
127
+
128
+ num_inference_steps = gr.Slider(
129
+ label="Number of inference steps",
130
+ minimum=1,
131
+ maximum=50,
132
+ step=1,
133
+ value=2, # Replace with defaults that work for your model
134
+ )
135
+
136
+ gr.Examples(examples=examples, inputs=[prompt])
137
+ gr.on(
138
+ triggers=[run_button.click, prompt.submit],
139
+ fn=infer,
140
+ inputs=[
141
+ prompt,
142
+ negative_prompt,
143
+ seed,
144
+ randomize_seed,
145
+ width,
146
+ height,
147
+ guidance_scale,
148
+ num_inference_steps,
149
+ ],
150
+ outputs=[result, seed],
151
+ )
152
+
153
+ if __name__ == "__main__":
154
+ demo.launch()
sepia.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import gradio as gr
3
+
4
+
5
+ def sepia(input_img):
6
+ sepia_filter = np.array([
7
+ [0.393, 0.769, 0.189],
8
+ [0.349, 0.686, 0.168],
9
+ [0.272, 0.534, 0.131]
10
+ ])
11
+ sepia_img = input_img.dot(sepia_filter.T)
12
+ sepia_img /= sepia_img.max()
13
+ return sepia_img
14
+
15
+
16
+ gr.ChatInterface(sepia, analytics_enabled=False)
17
+ demo = gr.Interface(sepia, gr.Image(), "image")
18
+ demo.launch(share=True)