-
Notifications
You must be signed in to change notification settings - Fork 0
/
arch_util.py
143 lines (115 loc) · 4.84 KB
/
arch_util.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import math
import collections.abc
from itertools import repeat
import torch
from torch import nn as nn
from torch.nn import functional as F
from torch.nn import init as init
from torch.nn.modules.batchnorm import _BatchNorm
# from pyiqa.utils.download_util import load_file_from_url
# --------------------------------------------
# IQA utils
# --------------------------------------------
def dist_to_mos(dist_score: torch.Tensor) -> torch.Tensor:
"""Convert distribution prediction to mos score.
For datasets with detailed score labels, such as AVA
Args:
dist_score (tensor): (*, C), C is the class number
Output:
mos_score (tensor): (*, 1)
"""
num_classes = dist_score.shape[-1]
mos_score = dist_score * torch.arange(1, num_classes + 1).to(dist_score)
mos_score = mos_score.sum(dim=-1, keepdim=True)
return mos_score
# --------------------------------------------
# Common utils
# --------------------------------------------
def load_pretrained_network(net, model_path, strict=True, weight_keys=None):
if model_path.startswith('https://') or model_path.startswith('http://'):
model_path = load_file_from_url(model_path)
print(f'Loading pretrained model {net.__class__.__name__} from {model_path}')
state_dict = torch.load(model_path, map_location=torch.device('cpu'))
if weight_keys:
state_dict = state_dict[weight_keys]
net.load_state_dict(state_dict, strict=strict)
def _ntuple(n):
def parse(x):
if isinstance(x, collections.abc.Iterable):
return x
return tuple(repeat(x, n))
return parse
to_1tuple = _ntuple(1)
to_2tuple = _ntuple(2)
to_3tuple = _ntuple(3)
to_4tuple = _ntuple(4)
to_ntuple = _ntuple
@torch.no_grad()
def default_init_weights(module_list, scale=1, bias_fill=0, **kwargs):
r"""Initialize network weights.
Args:
module_list (list[nn.Module] | nn.Module): Modules to be initialized.
scale (float): Scale initialized weights, especially for residual
blocks. Default: 1.
bias_fill (float): The value to fill bias. Default: 0.
kwargs (dict): Other arguments for initialization function.
"""
if not isinstance(module_list, list):
module_list = [module_list]
for module in module_list:
for m in module.modules():
if isinstance(m, nn.Conv2d):
init.kaiming_normal_(m.weight, **kwargs)
m.weight.data *= scale
if m.bias is not None:
m.bias.data.fill_(bias_fill)
elif isinstance(m, nn.Linear):
init.kaiming_normal_(m.weight, **kwargs)
m.weight.data *= scale
if m.bias is not None:
m.bias.data.fill_(bias_fill)
elif isinstance(m, _BatchNorm):
init.constant_(m.weight, 1)
if m.bias is not None:
m.bias.data.fill_(bias_fill)
def excact_padding_2d(x, kernel, stride=1, dilation=1, mode='same'):
assert len(x.shape) == 4, f'Only support 4D tensor input, but got {x.shape}'
kernel = to_2tuple(kernel)
stride = to_2tuple(stride)
dilation = to_2tuple(dilation)
b, c, h, w = x.shape
h2 = math.ceil(h / stride[0])
w2 = math.ceil(w / stride[1])
pad_row = (h2 - 1) * stride[0] + (kernel[0] - 1) * dilation[0] + 1 - h
pad_col = (w2 - 1) * stride[1] + (kernel[1] - 1) * dilation[1] + 1 - w
pad_l, pad_r, pad_t, pad_b = (pad_col // 2, pad_col - pad_col // 2, pad_row // 2, pad_row - pad_row // 2)
mode = mode if mode != 'same' else 'constant'
if mode != 'symmetric':
x = F.pad(x, (pad_l, pad_r, pad_t, pad_b), mode=mode)
elif mode == 'symmetric':
sym_h = torch.flip(x, [2])
sym_w = torch.flip(x, [3])
sym_hw = torch.flip(x, [2, 3])
row1 = torch.cat((sym_hw, sym_h, sym_hw), dim=3)
row2 = torch.cat((sym_w, x, sym_w), dim=3)
row3 = torch.cat((sym_hw, sym_h, sym_hw), dim=3)
whole_map = torch.cat((row1, row2, row3), dim=2)
x = whole_map[:, :, h - pad_t:2 * h + pad_b, w - pad_l:2 * w + pad_r, ]
return x
class ExactPadding2d(nn.Module):
r"""This function calculate exact padding values for 4D tensor inputs,
and support the same padding mode as tensorflow.
Args:
kernel (int or tuple): kernel size.
stride (int or tuple): stride size.
dilation (int or tuple): dilation size, default with 1.
mode (srt): padding mode can be ('same', 'symmetric', 'replicate', 'circular')
"""
def __init__(self, kernel, stride=1, dilation=1, mode='same'):
super().__init__()
self.kernel = to_2tuple(kernel)
self.stride = to_2tuple(stride)
self.dilation = to_2tuple(dilation)
self.mode = mode
def forward(self, x):
return excact_padding_2d(x, self.kernel, self.stride, self.dilation, self.mode)