Nymbo commited on
Commit
693b97f
·
verified ·
1 Parent(s): 942c9ea

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +863 -0
app.py ADDED
@@ -0,0 +1,863 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import collections
2
+ import json
3
+ import math
4
+ import os
5
+
6
+ import cv2
7
+ import gradio as gr
8
+ import numpy as np
9
+ import torch
10
+ import torch.nn as nn
11
+ import torch.nn.functional as F
12
+ from huggingface_hub import hf_hub_download
13
+
14
+ DEFAULT_REPO_ID = "piddnad/ddcolor_modelscope"
15
+
16
+ _COLORIZER_STATE = {
17
+ "initialized": False,
18
+ "pipeline": None,
19
+ }
20
+
21
+
22
+ def _resolve_device(device=None):
23
+ if device is None:
24
+ return torch.device("cuda" if torch.cuda.is_available() else "cpu")
25
+ if isinstance(device, str):
26
+ return torch.device(device)
27
+ return device
28
+
29
+
30
+ def _load_checkpoint_state_dict(model_path, map_location="cpu"):
31
+ checkpoint = torch.load(model_path, map_location=map_location)
32
+ if isinstance(checkpoint, dict):
33
+ if "params" in checkpoint:
34
+ return checkpoint["params"]
35
+ if "state_dict" in checkpoint:
36
+ return checkpoint["state_dict"]
37
+ return checkpoint
38
+
39
+
40
+ def _load_model_config(config_path):
41
+ with open(config_path, "r", encoding="utf-8") as handle:
42
+ return json.load(handle)
43
+
44
+
45
+ class DropPath(nn.Module):
46
+ def __init__(self, drop_prob=0.0):
47
+ super().__init__()
48
+ self.drop_prob = float(drop_prob)
49
+
50
+ def forward(self, x):
51
+ if self.drop_prob == 0.0 or not self.training:
52
+ return x
53
+ keep_prob = 1.0 - self.drop_prob
54
+ shape = (x.shape[0],) + (1,) * (x.ndim - 1)
55
+ random_tensor = keep_prob + torch.rand(shape, dtype=x.dtype, device=x.device)
56
+ random_tensor.floor_()
57
+ return x.div(keep_prob) * random_tensor
58
+
59
+
60
+ def trunc_normal_(tensor, mean=0.0, std=1.0, a=-2.0, b=2.0):
61
+ if hasattr(torch.nn.init, "trunc_normal_"):
62
+ return torch.nn.init.trunc_normal_(tensor, mean=mean, std=std, a=a, b=b)
63
+
64
+ def norm_cdf(value):
65
+ return (1.0 + math.erf(value / math.sqrt(2.0))) / 2.0
66
+
67
+ with torch.no_grad():
68
+ lower = norm_cdf((a - mean) / std)
69
+ upper = norm_cdf((b - mean) / std)
70
+ tensor.uniform_(2 * lower - 1, 2 * upper - 1)
71
+ tensor.erfinv_()
72
+ tensor.mul_(std * math.sqrt(2.0))
73
+ tensor.add_(mean)
74
+ tensor.clamp_(min=a, max=b)
75
+ return tensor
76
+
77
+
78
+ class LayerNorm(nn.Module):
79
+ def __init__(self, normalized_shape, eps=1e-6, data_format="channels_last"):
80
+ super().__init__()
81
+ self.weight = nn.Parameter(torch.ones(normalized_shape))
82
+ self.bias = nn.Parameter(torch.zeros(normalized_shape))
83
+ self.eps = eps
84
+ self.data_format = data_format
85
+ self.normalized_shape = (normalized_shape,)
86
+
87
+ def forward(self, x):
88
+ if self.data_format == "channels_last":
89
+ return F.layer_norm(x, self.normalized_shape, self.weight, self.bias, self.eps)
90
+ if self.data_format == "channels_first":
91
+ mean = x.mean(1, keepdim=True)
92
+ variance = (x - mean).pow(2).mean(1, keepdim=True)
93
+ x = (x - mean) / torch.sqrt(variance + self.eps)
94
+ return self.weight[:, None, None] * x + self.bias[:, None, None]
95
+ raise NotImplementedError(f"Unsupported data_format: {self.data_format}")
96
+
97
+
98
+ class ConvNeXtBlock(nn.Module):
99
+ def __init__(self, dim, drop_path=0.0, layer_scale_init_value=1e-6):
100
+ super().__init__()
101
+ self.dwconv = nn.Conv2d(dim, dim, kernel_size=7, padding=3, groups=dim)
102
+ self.norm = LayerNorm(dim, eps=1e-6)
103
+ self.pwconv1 = nn.Linear(dim, 4 * dim)
104
+ self.act = nn.GELU()
105
+ self.pwconv2 = nn.Linear(4 * dim, dim)
106
+ self.gamma = (
107
+ nn.Parameter(layer_scale_init_value * torch.ones((dim)), requires_grad=True)
108
+ if layer_scale_init_value > 0
109
+ else None
110
+ )
111
+ self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity()
112
+
113
+ def forward(self, x):
114
+ residual = x
115
+ x = self.dwconv(x)
116
+ x = x.permute(0, 2, 3, 1)
117
+ x = self.norm(x)
118
+ x = self.pwconv1(x)
119
+ x = self.act(x)
120
+ x = self.pwconv2(x)
121
+ if self.gamma is not None:
122
+ x = self.gamma * x
123
+ x = x.permute(0, 3, 1, 2)
124
+ return residual + self.drop_path(x)
125
+
126
+
127
+ class ConvNeXt(nn.Module):
128
+ def __init__(
129
+ self,
130
+ in_chans=3,
131
+ depths=(3, 3, 9, 3),
132
+ dims=(96, 192, 384, 768),
133
+ drop_path_rate=0.0,
134
+ layer_scale_init_value=1e-6,
135
+ ):
136
+ super().__init__()
137
+ self.downsample_layers = nn.ModuleList()
138
+ stem = nn.Sequential(
139
+ nn.Conv2d(in_chans, dims[0], kernel_size=4, stride=4),
140
+ LayerNorm(dims[0], eps=1e-6, data_format="channels_first"),
141
+ )
142
+ self.downsample_layers.append(stem)
143
+ for index in range(3):
144
+ self.downsample_layers.append(
145
+ nn.Sequential(
146
+ LayerNorm(dims[index], eps=1e-6, data_format="channels_first"),
147
+ nn.Conv2d(dims[index], dims[index + 1], kernel_size=2, stride=2),
148
+ )
149
+ )
150
+
151
+ self.stages = nn.ModuleList()
152
+ rates = [value.item() for value in torch.linspace(0, drop_path_rate, sum(depths))]
153
+ cursor = 0
154
+ for index in range(4):
155
+ stage = nn.Sequential(
156
+ *[
157
+ ConvNeXtBlock(
158
+ dim=dims[index],
159
+ drop_path=rates[cursor + inner],
160
+ layer_scale_init_value=layer_scale_init_value,
161
+ )
162
+ for inner in range(depths[index])
163
+ ]
164
+ )
165
+ self.stages.append(stage)
166
+ cursor += depths[index]
167
+
168
+ for index in range(4):
169
+ self.add_module(
170
+ f"norm{index}",
171
+ LayerNorm(dims[index], eps=1e-6, data_format="channels_first"),
172
+ )
173
+
174
+ self.norm = nn.LayerNorm(dims[-1], eps=1e-6)
175
+ self.apply(self._init_weights)
176
+
177
+ def _init_weights(self, module):
178
+ if isinstance(module, (nn.Conv2d, nn.Linear)):
179
+ trunc_normal_(module.weight, std=0.02)
180
+ nn.init.constant_(module.bias, 0)
181
+
182
+ def forward(self, x):
183
+ for index in range(4):
184
+ x = self.downsample_layers[index](x)
185
+ x = self.stages[index](x)
186
+ getattr(self, f"norm{index}")(x)
187
+ return self.norm(x.mean([-2, -1]))
188
+
189
+
190
+ class PositionEmbeddingSine(nn.Module):
191
+ def __init__(self, num_pos_feats=64, temperature=10000, normalize=False, scale=None):
192
+ super().__init__()
193
+ self.num_pos_feats = num_pos_feats
194
+ self.temperature = temperature
195
+ self.normalize = normalize
196
+ self.scale = scale if scale is not None else 2 * math.pi
197
+
198
+ def forward(self, x, mask=None):
199
+ if mask is None:
200
+ mask = torch.zeros(
201
+ (x.size(0), x.size(2), x.size(3)),
202
+ device=x.device,
203
+ dtype=torch.bool,
204
+ )
205
+ not_mask = ~mask
206
+ y_embed = not_mask.cumsum(1, dtype=torch.float32)
207
+ x_embed = not_mask.cumsum(2, dtype=torch.float32)
208
+ if self.normalize:
209
+ eps = 1e-6
210
+ y_embed = y_embed / (y_embed[:, -1:, :] + eps) * self.scale
211
+ x_embed = x_embed / (x_embed[:, :, -1:] + eps) * self.scale
212
+
213
+ dim_t = torch.arange(self.num_pos_feats, dtype=torch.float32, device=x.device)
214
+ dim_t = self.temperature ** (2 * (dim_t // 2) / self.num_pos_feats)
215
+
216
+ pos_x = x_embed[:, :, :, None] / dim_t
217
+ pos_y = y_embed[:, :, :, None] / dim_t
218
+ pos_x = torch.stack((pos_x[:, :, :, 0::2].sin(), pos_x[:, :, :, 1::2].cos()), dim=4).flatten(3)
219
+ pos_y = torch.stack((pos_y[:, :, :, 0::2].sin(), pos_y[:, :, :, 1::2].cos()), dim=4).flatten(3)
220
+ return torch.cat((pos_y, pos_x), dim=3).permute(0, 3, 1, 2)
221
+
222
+
223
+ class SelfAttentionLayer(nn.Module):
224
+ def __init__(self, d_model, nhead, dropout=0.0, normalize_before=False):
225
+ super().__init__()
226
+ self.self_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout)
227
+ self.norm = nn.LayerNorm(d_model)
228
+ self.dropout = nn.Dropout(dropout)
229
+ self.normalize_before = normalize_before
230
+ self._reset_parameters()
231
+
232
+ def _reset_parameters(self):
233
+ for parameter in self.parameters():
234
+ if parameter.dim() > 1:
235
+ nn.init.xavier_uniform_(parameter)
236
+
237
+ def _with_pos_embed(self, tensor, pos):
238
+ return tensor if pos is None else tensor + pos
239
+
240
+ def forward(self, target, tgt_mask=None, tgt_key_padding_mask=None, query_pos=None):
241
+ if self.normalize_before:
242
+ target_norm = self.norm(target)
243
+ query = key = self._with_pos_embed(target_norm, query_pos)
244
+ target2 = self.self_attn(
245
+ query,
246
+ key,
247
+ value=target_norm,
248
+ attn_mask=tgt_mask,
249
+ key_padding_mask=tgt_key_padding_mask,
250
+ )[0]
251
+ return target + self.dropout(target2)
252
+
253
+ query = key = self._with_pos_embed(target, query_pos)
254
+ target2 = self.self_attn(
255
+ query,
256
+ key,
257
+ value=target,
258
+ attn_mask=tgt_mask,
259
+ key_padding_mask=tgt_key_padding_mask,
260
+ )[0]
261
+ target = target + self.dropout(target2)
262
+ return self.norm(target)
263
+
264
+
265
+ class CrossAttentionLayer(nn.Module):
266
+ def __init__(self, d_model, nhead, dropout=0.0, normalize_before=False):
267
+ super().__init__()
268
+ self.multihead_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout)
269
+ self.norm = nn.LayerNorm(d_model)
270
+ self.dropout = nn.Dropout(dropout)
271
+ self.normalize_before = normalize_before
272
+ self._reset_parameters()
273
+
274
+ def _reset_parameters(self):
275
+ for parameter in self.parameters():
276
+ if parameter.dim() > 1:
277
+ nn.init.xavier_uniform_(parameter)
278
+
279
+ def _with_pos_embed(self, tensor, pos):
280
+ return tensor if pos is None else tensor + pos
281
+
282
+ def forward(
283
+ self,
284
+ target,
285
+ memory,
286
+ memory_mask=None,
287
+ memory_key_padding_mask=None,
288
+ pos=None,
289
+ query_pos=None,
290
+ ):
291
+ if self.normalize_before:
292
+ target_norm = self.norm(target)
293
+ target2 = self.multihead_attn(
294
+ query=self._with_pos_embed(target_norm, query_pos),
295
+ key=self._with_pos_embed(memory, pos),
296
+ value=memory,
297
+ attn_mask=memory_mask,
298
+ key_padding_mask=memory_key_padding_mask,
299
+ )[0]
300
+ return target + self.dropout(target2)
301
+
302
+ target2 = self.multihead_attn(
303
+ query=self._with_pos_embed(target, query_pos),
304
+ key=self._with_pos_embed(memory, pos),
305
+ value=memory,
306
+ attn_mask=memory_mask,
307
+ key_padding_mask=memory_key_padding_mask,
308
+ )[0]
309
+ target = target + self.dropout(target2)
310
+ return self.norm(target)
311
+
312
+
313
+ class FFNLayer(nn.Module):
314
+ def __init__(self, d_model, dim_feedforward=2048, dropout=0.0, normalize_before=False):
315
+ super().__init__()
316
+ self.linear1 = nn.Linear(d_model, dim_feedforward)
317
+ self.dropout = nn.Dropout(dropout)
318
+ self.linear2 = nn.Linear(dim_feedforward, d_model)
319
+ self.norm = nn.LayerNorm(d_model)
320
+ self.normalize_before = normalize_before
321
+ self._reset_parameters()
322
+
323
+ def _reset_parameters(self):
324
+ for parameter in self.parameters():
325
+ if parameter.dim() > 1:
326
+ nn.init.xavier_uniform_(parameter)
327
+
328
+ def forward(self, target):
329
+ if self.normalize_before:
330
+ target_norm = self.norm(target)
331
+ target2 = self.linear2(self.dropout(F.relu(self.linear1(target_norm))))
332
+ return target + self.dropout(target2)
333
+
334
+ target2 = self.linear2(self.dropout(F.relu(self.linear1(target))))
335
+ target = target + self.dropout(target2)
336
+ return self.norm(target)
337
+
338
+
339
+ class MLP(nn.Module):
340
+ def __init__(self, input_dim, hidden_dim, output_dim, num_layers):
341
+ super().__init__()
342
+ widths = [hidden_dim] * (num_layers - 1)
343
+ self.layers = nn.ModuleList(
344
+ nn.Linear(in_features, out_features)
345
+ for in_features, out_features in zip(
346
+ [input_dim] + widths,
347
+ widths + [output_dim],
348
+ )
349
+ )
350
+
351
+ def forward(self, x):
352
+ for index, layer in enumerate(self.layers):
353
+ x = F.relu(layer(x)) if index < len(self.layers) - 1 else layer(x)
354
+ return x
355
+
356
+
357
+ class Hook:
358
+ feature = None
359
+
360
+ def __init__(self, module):
361
+ self.hook = module.register_forward_hook(self._hook_fn)
362
+
363
+ def _hook_fn(self, module, inputs, output):
364
+ if isinstance(output, torch.Tensor):
365
+ self.feature = output
366
+ elif isinstance(output, collections.OrderedDict):
367
+ self.feature = output["out"]
368
+
369
+ def remove(self):
370
+ self.hook.remove()
371
+
372
+
373
+ class NormType:
374
+ Spectral = "Spectral"
375
+
376
+
377
+ def _batchnorm_2d(num_features):
378
+ batch_norm = nn.BatchNorm2d(num_features)
379
+ with torch.no_grad():
380
+ batch_norm.bias.fill_(1e-3)
381
+ batch_norm.weight.fill_(1.0)
382
+ return batch_norm
383
+
384
+
385
+ def _init_default(module, init=nn.init.kaiming_normal_):
386
+ if init is not None:
387
+ if hasattr(module, "weight"):
388
+ init(module.weight)
389
+ if hasattr(module, "bias") and hasattr(module.bias, "data"):
390
+ module.bias.data.fill_(0.0)
391
+ return module
392
+
393
+
394
+ def _icnr(tensor, scale=2, init=nn.init.kaiming_normal_):
395
+ in_channels, out_channels, height, width = tensor.shape
396
+ in_channels_scaled = int(in_channels / (scale**2))
397
+ kernel = init(torch.zeros([in_channels_scaled, out_channels, height, width])).transpose(0, 1)
398
+ kernel = kernel.contiguous().view(in_channels_scaled, out_channels, -1)
399
+ kernel = kernel.repeat(1, 1, scale**2)
400
+ kernel = kernel.contiguous().view([out_channels, in_channels, height, width]).transpose(0, 1)
401
+ tensor.data.copy_(kernel)
402
+
403
+
404
+ def _custom_conv_layer(
405
+ in_channels,
406
+ out_channels,
407
+ ks=3,
408
+ stride=1,
409
+ padding=None,
410
+ bias=None,
411
+ norm_type=NormType.Spectral,
412
+ use_activation=True,
413
+ transpose=False,
414
+ extra_bn=False,
415
+ ):
416
+ if padding is None:
417
+ padding = (ks - 1) // 2 if not transpose else 0
418
+ use_batch_norm = extra_bn
419
+ if bias is None:
420
+ bias = not use_batch_norm
421
+ conv_cls = nn.ConvTranspose2d if transpose else nn.Conv2d
422
+ conv = _init_default(
423
+ conv_cls(in_channels, out_channels, kernel_size=ks, bias=bias, stride=stride, padding=padding)
424
+ )
425
+ if norm_type == NormType.Spectral:
426
+ conv = nn.utils.spectral_norm(conv)
427
+ layers = [conv]
428
+ if use_activation:
429
+ layers.append(nn.ReLU(True))
430
+ if use_batch_norm:
431
+ layers.append(nn.BatchNorm2d(out_channels))
432
+ return nn.Sequential(*layers)
433
+
434
+
435
+ class CustomPixelShuffleICNR(nn.Module):
436
+ def __init__(self, in_channels, out_channels, scale=2, blur=True, norm_type=NormType.Spectral, extra_bn=False):
437
+ super().__init__()
438
+ self.conv = _custom_conv_layer(
439
+ in_channels,
440
+ out_channels * (scale**2),
441
+ ks=1,
442
+ use_activation=False,
443
+ norm_type=norm_type,
444
+ extra_bn=extra_bn,
445
+ )
446
+ _icnr(self.conv[0].weight)
447
+ self.shuffle = nn.PixelShuffle(scale)
448
+ self.blur_enabled = blur
449
+ self.pad = nn.ReplicationPad2d((1, 0, 1, 0))
450
+ self.blur = nn.AvgPool2d(2, stride=1)
451
+ self.relu = nn.ReLU(True)
452
+
453
+ def forward(self, x):
454
+ x = self.shuffle(self.relu(self.conv(x)))
455
+ return self.blur(self.pad(x)) if self.blur_enabled else x
456
+
457
+
458
+ class UnetBlockWide(nn.Module):
459
+ def __init__(self, up_in_channels, skip_in_channels, out_channels, hook, blur=False, norm_type=NormType.Spectral):
460
+ super().__init__()
461
+ self.hook = hook
462
+ self.shuf = CustomPixelShuffleICNR(
463
+ up_in_channels,
464
+ out_channels,
465
+ blur=blur,
466
+ norm_type=norm_type,
467
+ extra_bn=True,
468
+ )
469
+ self.bn = _batchnorm_2d(skip_in_channels)
470
+ self.conv = _custom_conv_layer(
471
+ out_channels + skip_in_channels,
472
+ out_channels,
473
+ norm_type=norm_type,
474
+ extra_bn=True,
475
+ )
476
+ self.relu = nn.ReLU()
477
+
478
+ def forward(self, x):
479
+ skip = self.hook.feature
480
+ x = self.shuf(x)
481
+ x = self.relu(torch.cat([x, self.bn(skip)], dim=1))
482
+ return self.conv(x)
483
+
484
+
485
+ class ImageEncoder(nn.Module):
486
+ def __init__(self, encoder_name, hook_names):
487
+ super().__init__()
488
+ if encoder_name == "convnext-t":
489
+ self.arch = ConvNeXt(depths=(3, 3, 9, 3), dims=(96, 192, 384, 768))
490
+ elif encoder_name == "convnext-l":
491
+ self.arch = ConvNeXt(depths=(3, 3, 27, 3), dims=(192, 384, 768, 1536))
492
+ else:
493
+ raise NotImplementedError(f"Unsupported encoder: {encoder_name}")
494
+ self.hooks = [Hook(self.arch._modules[name]) for name in hook_names]
495
+
496
+ def forward(self, x):
497
+ return self.arch(x)
498
+
499
+
500
+ class MultiScaleColorDecoder(nn.Module):
501
+ def __init__(
502
+ self,
503
+ in_channels,
504
+ hidden_dim=256,
505
+ num_queries=100,
506
+ nheads=8,
507
+ dim_feedforward=2048,
508
+ dec_layers=9,
509
+ pre_norm=False,
510
+ color_embed_dim=256,
511
+ enforce_input_project=True,
512
+ num_scales=3,
513
+ ):
514
+ super().__init__()
515
+ self.num_layers = dec_layers
516
+ self.num_feature_levels = num_scales
517
+ self.pe_layer = PositionEmbeddingSine(hidden_dim // 2, normalize=True)
518
+ self.query_feat = nn.Embedding(num_queries, hidden_dim)
519
+ self.query_embed = nn.Embedding(num_queries, hidden_dim)
520
+ self.level_embed = nn.Embedding(num_scales, hidden_dim)
521
+ self.input_proj = nn.ModuleList()
522
+ for channels in in_channels:
523
+ if channels != hidden_dim or enforce_input_project:
524
+ projection = nn.Conv2d(channels, hidden_dim, kernel_size=1)
525
+ nn.init.kaiming_uniform_(projection.weight, a=1)
526
+ if projection.bias is not None:
527
+ nn.init.constant_(projection.bias, 0)
528
+ self.input_proj.append(projection)
529
+ else:
530
+ self.input_proj.append(nn.Sequential())
531
+
532
+ self.transformer_self_attention_layers = nn.ModuleList()
533
+ self.transformer_cross_attention_layers = nn.ModuleList()
534
+ self.transformer_ffn_layers = nn.ModuleList()
535
+ for _ in range(dec_layers):
536
+ self.transformer_self_attention_layers.append(
537
+ SelfAttentionLayer(hidden_dim, nheads, dropout=0.0, normalize_before=pre_norm)
538
+ )
539
+ self.transformer_cross_attention_layers.append(
540
+ CrossAttentionLayer(hidden_dim, nheads, dropout=0.0, normalize_before=pre_norm)
541
+ )
542
+ self.transformer_ffn_layers.append(
543
+ FFNLayer(hidden_dim, dim_feedforward=dim_feedforward, dropout=0.0, normalize_before=pre_norm)
544
+ )
545
+
546
+ self.decoder_norm = nn.LayerNorm(hidden_dim)
547
+ self.color_embed = MLP(hidden_dim, hidden_dim, color_embed_dim, 3)
548
+
549
+ def forward(self, features, image_features):
550
+ src = []
551
+ pos = []
552
+ for index, feature in enumerate(features):
553
+ pos.append(self.pe_layer(feature).flatten(2).permute(2, 0, 1))
554
+ src.append(
555
+ (
556
+ self.input_proj[index](feature).flatten(2)
557
+ + self.level_embed.weight[index][None, :, None]
558
+ ).permute(2, 0, 1)
559
+ )
560
+
561
+ _, batch_size, _ = src[0].shape
562
+ query_embed = self.query_embed.weight.unsqueeze(1).repeat(1, batch_size, 1)
563
+ output = self.query_feat.weight.unsqueeze(1).repeat(1, batch_size, 1)
564
+
565
+ for index in range(self.num_layers):
566
+ level_index = index % self.num_feature_levels
567
+ output = self.transformer_cross_attention_layers[index](
568
+ output,
569
+ src[level_index],
570
+ memory_mask=None,
571
+ memory_key_padding_mask=None,
572
+ pos=pos[level_index],
573
+ query_pos=query_embed,
574
+ )
575
+ output = self.transformer_self_attention_layers[index](
576
+ output,
577
+ tgt_mask=None,
578
+ tgt_key_padding_mask=None,
579
+ query_pos=query_embed,
580
+ )
581
+ output = self.transformer_ffn_layers[index](output)
582
+
583
+ decoder_output = self.decoder_norm(output).transpose(0, 1)
584
+ color_embed = self.color_embed(decoder_output)
585
+ return torch.einsum("bqc,bchw->bqhw", color_embed, image_features)
586
+
587
+
588
+ class DualDecoder(nn.Module):
589
+ def __init__(self, hooks, nf=512, blur=True, num_queries=100, num_scales=3, dec_layers=9):
590
+ super().__init__()
591
+ self.hooks = hooks
592
+ self.nf = nf
593
+ self.blur = blur
594
+ self.layers = self._make_layers()
595
+ embed_dim = nf // 2
596
+ self.last_shuf = CustomPixelShuffleICNR(
597
+ embed_dim,
598
+ embed_dim,
599
+ scale=4,
600
+ blur=self.blur,
601
+ norm_type=NormType.Spectral,
602
+ )
603
+ self.color_decoder = MultiScaleColorDecoder(
604
+ in_channels=[512, 512, 256],
605
+ num_queries=num_queries,
606
+ num_scales=num_scales,
607
+ dec_layers=dec_layers,
608
+ )
609
+
610
+ def _make_layers(self):
611
+ layers = []
612
+ in_channels = self.hooks[-1].feature.shape[1]
613
+ out_channels = self.nf
614
+ setup_hooks = self.hooks[-2::-1]
615
+ for index, hook in enumerate(setup_hooks):
616
+ skip_channels = hook.feature.shape[1]
617
+ if index == len(setup_hooks) - 1:
618
+ out_channels = out_channels // 2
619
+ layers.append(
620
+ UnetBlockWide(
621
+ in_channels,
622
+ skip_channels,
623
+ out_channels,
624
+ hook,
625
+ blur=self.blur,
626
+ norm_type=NormType.Spectral,
627
+ )
628
+ )
629
+ in_channels = out_channels
630
+ return nn.Sequential(*layers)
631
+
632
+ def forward(self):
633
+ encoded = self.hooks[-1].feature
634
+ out0 = self.layers[0](encoded)
635
+ out1 = self.layers[1](out0)
636
+ out2 = self.layers[2](out1)
637
+ out3 = self.last_shuf(out2)
638
+ return self.color_decoder([out0, out1, out2], out3)
639
+
640
+
641
+ class DDColor(nn.Module):
642
+ def __init__(
643
+ self,
644
+ encoder_name="convnext-l",
645
+ decoder_name="MultiScaleColorDecoder",
646
+ num_input_channels=3,
647
+ input_size=(256, 256),
648
+ nf=512,
649
+ num_output_channels=2,
650
+ last_norm="Spectral",
651
+ do_normalize=False,
652
+ num_queries=100,
653
+ num_scales=3,
654
+ dec_layers=9,
655
+ ):
656
+ super().__init__()
657
+ if decoder_name != "MultiScaleColorDecoder":
658
+ raise NotImplementedError(f"Unsupported decoder: {decoder_name}")
659
+ if last_norm != "Spectral":
660
+ raise NotImplementedError(f"Unsupported last_norm: {last_norm}")
661
+
662
+ self.encoder = ImageEncoder(encoder_name, ["norm0", "norm1", "norm2", "norm3"])
663
+ self.encoder.eval()
664
+ test_input = torch.randn(1, num_input_channels, *input_size)
665
+ with torch.no_grad():
666
+ self.encoder(test_input)
667
+
668
+ self.decoder = DualDecoder(
669
+ self.encoder.hooks,
670
+ nf=nf,
671
+ num_queries=num_queries,
672
+ num_scales=num_scales,
673
+ dec_layers=dec_layers,
674
+ )
675
+ self.refine_net = nn.Sequential(
676
+ _custom_conv_layer(
677
+ num_queries + 3,
678
+ num_output_channels,
679
+ ks=1,
680
+ use_activation=False,
681
+ norm_type=NormType.Spectral,
682
+ )
683
+ )
684
+ self.do_normalize = do_normalize
685
+ self.register_buffer("mean", torch.tensor([0.485, 0.456, 0.406]).view(1, 3, 1, 1))
686
+ self.register_buffer("std", torch.tensor([0.229, 0.224, 0.225]).view(1, 3, 1, 1))
687
+
688
+ def normalize(self, image):
689
+ return (image - self.mean) / self.std
690
+
691
+ def denormalize(self, image):
692
+ return image * self.std + self.mean
693
+
694
+ def forward(self, image):
695
+ if image.shape[1] == 3:
696
+ image = self.normalize(image)
697
+ self.encoder(image)
698
+ decoded = self.decoder()
699
+ coarse_input = torch.cat([decoded, image], dim=1)
700
+ output = self.refine_net(coarse_input)
701
+ if self.do_normalize:
702
+ output = self.denormalize(output)
703
+ return output
704
+
705
+
706
+ class ColorizationPipeline:
707
+ def __init__(self, model, input_size=512, device=None):
708
+ self.input_size = int(input_size)
709
+ self.device = _resolve_device(device)
710
+ self.model = model.to(self.device)
711
+ self.model.eval()
712
+
713
+ def process(self, image_bgr):
714
+ context = torch.inference_mode if hasattr(torch, "inference_mode") else torch.no_grad
715
+ with context():
716
+ if image_bgr is None:
717
+ raise ValueError("image is None")
718
+
719
+ height, width = image_bgr.shape[:2]
720
+ image = (image_bgr / 255.0).astype(np.float32)
721
+ orig_l = cv2.cvtColor(image, cv2.COLOR_BGR2Lab)[:, :, :1]
722
+
723
+ resized = cv2.resize(image, (self.input_size, self.input_size))
724
+ resized_l = cv2.cvtColor(resized, cv2.COLOR_BGR2Lab)[:, :, :1]
725
+ gray_lab = np.concatenate(
726
+ (resized_l, np.zeros_like(resized_l), np.zeros_like(resized_l)),
727
+ axis=-1,
728
+ )
729
+ gray_rgb = cv2.cvtColor(gray_lab, cv2.COLOR_LAB2RGB)
730
+ tensor = (
731
+ torch.from_numpy(gray_rgb.transpose((2, 0, 1)))
732
+ .float()
733
+ .unsqueeze(0)
734
+ .to(self.device)
735
+ )
736
+
737
+ output_ab = self.model(tensor).cpu()
738
+ resized_ab = (
739
+ F.interpolate(output_ab, size=(height, width))[0]
740
+ .float()
741
+ .numpy()
742
+ .transpose(1, 2, 0)
743
+ )
744
+ output_lab = np.concatenate((orig_l, resized_ab), axis=-1)
745
+ output_bgr = cv2.cvtColor(output_lab, cv2.COLOR_LAB2BGR)
746
+ return (output_bgr * 255.0).round().astype(np.uint8)
747
+
748
+
749
+ def build_colorizer(repo_id=DEFAULT_REPO_ID, device=None):
750
+ device = _resolve_device(device)
751
+ config_path = hf_hub_download(repo_id=repo_id, filename="config.json")
752
+ weights_path = hf_hub_download(repo_id=repo_id, filename="pytorch_model.bin")
753
+ config = _load_model_config(config_path)
754
+ model = DDColor(**config)
755
+ state_dict = _load_checkpoint_state_dict(weights_path, map_location="cpu")
756
+ model.load_state_dict(state_dict, strict=True)
757
+ model = model.to(device)
758
+ model.eval()
759
+ input_size = config.get("input_size", [512, 512])[0]
760
+ return ColorizationPipeline(model, input_size=input_size, device=device)
761
+
762
+
763
+ def _get_colorizer():
764
+ if _COLORIZER_STATE["initialized"]:
765
+ return _COLORIZER_STATE["pipeline"]
766
+
767
+ try:
768
+ colorizer = build_colorizer(
769
+ repo_id=os.getenv("DDCOLOR_REPO_ID", DEFAULT_REPO_ID),
770
+ )
771
+ except Exception as error:
772
+ raise gr.Error(
773
+ "Failed to initialize the DDColor model from Hugging Face Hub. "
774
+ f"Error: {str(error)[:200]}"
775
+ )
776
+
777
+ _COLORIZER_STATE.update(
778
+ {
779
+ "initialized": True,
780
+ "pipeline": colorizer,
781
+ }
782
+ )
783
+ return colorizer
784
+
785
+
786
+ def _normalize_input_image(image):
787
+ if image.ndim == 2:
788
+ return np.stack([image, image, image], axis=-1)
789
+ if image.shape[-1] == 4:
790
+ return image[..., :3]
791
+ return image
792
+
793
+
794
+ def color(image):
795
+ if image is None:
796
+ raise gr.Error("Please upload an image.")
797
+
798
+ image = _normalize_input_image(image)
799
+ colorizer = _get_colorizer()
800
+ result_bgr = colorizer.process(image[..., ::-1])
801
+ result_rgb = result_bgr[..., ::-1]
802
+ print("infer finished!")
803
+ return (image, result_rgb)
804
+
805
+
806
+ def clear_ui():
807
+ return None, None
808
+
809
+
810
+ examples = [["./input.jpg"]]
811
+
812
+ with gr.Blocks(fill_width=True) as demo:
813
+ with gr.Row():
814
+ with gr.Column():
815
+ input_image = gr.Image(
816
+ type="numpy",
817
+ label="Old Photo",
818
+ )
819
+
820
+ with gr.Row():
821
+ clear_btn = gr.Button("Clear")
822
+ submit_btn = gr.Button("Colorize", variant="primary")
823
+
824
+ with gr.Column():
825
+ comparison_output = gr.ImageSlider(
826
+ type="numpy",
827
+ slider_position=50,
828
+ label="Before / After",
829
+ )
830
+
831
+ gr.Examples(
832
+ examples=examples,
833
+ inputs=input_image,
834
+ outputs=comparison_output,
835
+ fn=color,
836
+ cache_examples=True,
837
+ cache_mode="eager",
838
+ preload=0,
839
+ )
840
+
841
+ submit_btn.click(
842
+ fn=color,
843
+ inputs=input_image,
844
+ outputs=comparison_output,
845
+ )
846
+
847
+ input_image.input(
848
+ fn=lambda: None,
849
+ outputs=comparison_output,
850
+ )
851
+
852
+ clear_btn.click(
853
+ fn=clear_ui,
854
+ outputs=[input_image, comparison_output],
855
+ )
856
+
857
+ if __name__ == "__main__":
858
+ demo.queue().launch(
859
+ share=False,
860
+ ssr_mode=False,
861
+ theme="Nymbo/Nymbo_Theme",
862
+ footer_links=[],
863
+ )