codermert commited on
Commit
b209bd4
·
verified ·
1 Parent(s): e8ce71f

Create arch_utils.py

Browse files
Files changed (1) hide show
  1. RealESRGAN/arch_utils.py +185 -0
RealESRGAN/arch_utils.py ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ import torch
3
+ from torch import nn as nn
4
+ from torch.nn import functional as F
5
+ from torch.nn import init as init
6
+ from torch.nn.modules.batchnorm import _BatchNorm
7
+
8
+ @torch.no_grad()
9
+ def default_init_weights(module_list, scale=1, bias_fill=0, **kwargs):
10
+ """Initialize network weights.
11
+ Args:
12
+ module_list (list[nn.Module] | nn.Module): Modules to be initialized.
13
+ scale (float): Scale initialized weights, especially for residual
14
+ blocks. Default: 1.
15
+ bias_fill (float): The value to fill bias. Default: 0
16
+ kwargs (dict): Other arguments for initialization function.
17
+ """
18
+ if not isinstance(module_list, list):
19
+ module_list = [module_list]
20
+ for module in module_list:
21
+ for m in module.modules():
22
+ if isinstance(m, nn.Conv2d):
23
+ init.kaiming_normal_(m.weight, **kwargs)
24
+ m.weight.data *= scale
25
+ if m.bias is not None:
26
+ m.bias.data.fill_(bias_fill)
27
+ elif isinstance(m, nn.Linear):
28
+ init.kaiming_normal_(m.weight, **kwargs)
29
+ m.weight.data *= scale
30
+ if m.bias is not None:
31
+ m.bias.data.fill_(bias_fill)
32
+ elif isinstance(m, _BatchNorm):
33
+ init.constant_(m.weight, 1)
34
+ if m.bias is not None:
35
+ m.bias.data.fill_(bias_fill)
36
+
37
+
38
+ def make_layer(basic_block, num_basic_block, **kwarg):
39
+ """Make layers by stacking the same blocks.
40
+ Args:
41
+ basic_block (nn.module): nn.module class for basic block.
42
+ num_basic_block (int): number of blocks.
43
+ Returns:
44
+ nn.Sequential: Stacked blocks in nn.Sequential.
45
+ """
46
+ layers = []
47
+ for _ in range(num_basic_block):
48
+ layers.append(basic_block(**kwarg))
49
+ return nn.Sequential(*layers)
50
+
51
+
52
+ class ResidualBlockNoBN(nn.Module):
53
+ """Residual block without BN.
54
+ It has a style of:
55
+ ---Conv-ReLU-Conv-+-
56
+ |________________|
57
+ Args:
58
+ num_feat (int): Channel number of intermediate features.
59
+ Default: 64.
60
+ res_scale (float): Residual scale. Default: 1.
61
+ pytorch_init (bool): If set to True, use pytorch default init,
62
+ otherwise, use default_init_weights. Default: False.
63
+ """
64
+
65
+ def __init__(self, num_feat=64, res_scale=1, pytorch_init=False):
66
+ super(ResidualBlockNoBN, self).__init__()
67
+ self.res_scale = res_scale
68
+ self.conv1 = nn.Conv2d(num_feat, num_feat, 3, 1, 1, bias=True)
69
+ self.conv2 = nn.Conv2d(num_feat, num_feat, 3, 1, 1, bias=True)
70
+ self.relu = nn.ReLU(inplace=True)
71
+
72
+ if not pytorch_init:
73
+ default_init_weights([self.conv1, self.conv2], 0.1)
74
+
75
+ def forward(self, x):
76
+ identity = x
77
+ out = self.conv2(self.relu(self.conv1(x)))
78
+ return identity + out * self.res_scale
79
+
80
+
81
+ class Upsample(nn.Sequential):
82
+ """Upsample module.
83
+ Args:
84
+ scale (int): Scale factor. Supported scales: 2^n and 3.
85
+ num_feat (int): Channel number of intermediate features.
86
+ """
87
+
88
+ def __init__(self, scale, num_feat):
89
+ m = []
90
+ if (scale & (scale - 1)) == 0: # scale = 2^n
91
+ for _ in range(int(math.log(scale, 2))):
92
+ m.append(nn.Conv2d(num_feat, 4 * num_feat, 3, 1, 1))
93
+ m.append(nn.PixelShuffle(2))
94
+ elif scale == 3:
95
+ m.append(nn.Conv2d(num_feat, 9 * num_feat, 3, 1, 1))
96
+ m.append(nn.PixelShuffle(3))
97
+ else:
98
+ raise ValueError(f'scale {scale} is not supported. ' 'Supported scales: 2^n and 3.')
99
+ super(Upsample, self).__init__(*m)
100
+
101
+
102
+ def flow_warp(x, flow, interp_mode='bilinear', padding_mode='zeros', align_corners=True):
103
+ """Warp an image or feature map with optical flow.
104
+ Args:
105
+ x (Tensor): Tensor with size (n, c, h, w).
106
+ flow (Tensor): Tensor with size (n, h, w, 2), normal value.
107
+ interp_mode (str): 'nearest' or 'bilinear'. Default: 'bilinear'.
108
+ padding_mode (str): 'zeros' or 'border' or 'reflection'.
109
+ Default: 'zeros'.
110
+ align_corners (bool): Before pytorch 1.3, the default value is
111
+ align_corners=True. After pytorch 1.3, the default value is
112
+ align_corners=False. Here, we use the True as default.
113
+ Returns:
114
+ Tensor: Warped image or feature map.
115
+ """
116
+ assert x.size()[-2:] == flow.size()[1:3]
117
+ _, _, h, w = x.size()
118
+ # create mesh grid
119
+ grid_y, grid_x = torch.meshgrid(torch.arange(0, h).type_as(x), torch.arange(0, w).type_as(x))
120
+ grid = torch.stack((grid_x, grid_y), 2).float() # W(x), H(y), 2
121
+ grid.requires_grad = False
122
+
123
+ vgrid = grid + flow
124
+ # scale grid to [-1,1]
125
+ vgrid_x = 2.0 * vgrid[:, :, :, 0] / max(w - 1, 1) - 1.0
126
+ vgrid_y = 2.0 * vgrid[:, :, :, 1] / max(h - 1, 1) - 1.0
127
+ vgrid_scaled = torch.stack((vgrid_x, vgrid_y), dim=3)
128
+ output = F.grid_sample(x, vgrid_scaled, mode=interp_mode, padding_mode=padding_mode, align_corners=align_corners)
129
+
130
+ # TODO, what if align_corners=False
131
+ return output
132
+
133
+
134
+ def resize_flow(flow, size_type, sizes, interp_mode='bilinear', align_corners=False):
135
+ """Resize a flow according to ratio or shape.
136
+ Args:
137
+ flow (Tensor): Precomputed flow. shape [N, 2, H, W].
138
+ size_type (str): 'ratio' or 'shape'.
139
+ sizes (list[int | float]): the ratio for resizing or the final output
140
+ shape.
141
+ 1) The order of ratio should be [ratio_h, ratio_w]. For
142
+ downsampling, the ratio should be smaller than 1.0 (i.e., ratio
143
+ < 1.0). For upsampling, the ratio should be larger than 1.0 (i.e.,
144
+ ratio > 1.0).
145
+ 2) The order of output_size should be [out_h, out_w].
146
+ interp_mode (str): The mode of interpolation for resizing.
147
+ Default: 'bilinear'.
148
+ align_corners (bool): Whether align corners. Default: False.
149
+ Returns:
150
+ Tensor: Resized flow.
151
+ """
152
+ _, _, flow_h, flow_w = flow.size()
153
+ if size_type == 'ratio':
154
+ output_h, output_w = int(flow_h * sizes[0]), int(flow_w * sizes[1])
155
+ elif size_type == 'shape':
156
+ output_h, output_w = sizes[0], sizes[1]
157
+ else:
158
+ raise ValueError(f'Size type should be ratio or shape, but got type {size_type}.')
159
+
160
+ input_flow = flow.clone()
161
+ ratio_h = output_h / flow_h
162
+ ratio_w = output_w / flow_w
163
+ input_flow[:, 0, :, :] *= ratio_w
164
+ input_flow[:, 1, :, :] *= ratio_h
165
+ resized_flow = F.interpolate(
166
+ input=input_flow, size=(output_h, output_w), mode=interp_mode, align_corners=align_corners)
167
+ return resized_flow
168
+
169
+
170
+ # TODO: may write a cpp file
171
+ def pixel_unshuffle(x, scale):
172
+ """ Pixel unshuffle.
173
+ Args:
174
+ x (Tensor): Input feature with shape (b, c, hh, hw).
175
+ scale (int): Downsample ratio.
176
+ Returns:
177
+ Tensor: the pixel unshuffled feature.
178
+ """
179
+ b, c, hh, hw = x.size()
180
+ out_channel = c * (scale**2)
181
+ assert hh % scale == 0 and hw % scale == 0
182
+ h = hh // scale
183
+ w = hw // scale
184
+ x_view = x.view(b, c, h, scale, w, scale)
185
+ return x_view.permute(0, 1, 3, 5, 2, 4).reshape(b, out_channel, h, w)