hank1996 commited on
Commit
da8afdf
·
1 Parent(s): 5ac09ab

Create new file

Browse files
Files changed (1) hide show
  1. lib/models/common.py +272 -0
lib/models/common.py ADDED
@@ -0,0 +1,272 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ import math
4
+ import numpy as np
5
+ import torch
6
+ import torch.nn as nn
7
+ from PIL import Image, ImageDraw
8
+ import torch.nn.functional as F
9
+
10
+
11
+ def autopad(k, p=None): # kernel, padding
12
+ # Pad to 'same'
13
+ if p is None:
14
+ p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad
15
+ return p
16
+
17
+
18
+ class DepthSeperabelConv2d(nn.Module):
19
+ """
20
+ DepthSeperable Convolution 2d with residual connection
21
+ """
22
+
23
+ def __init__(self, inplanes, planes, kernel_size=3, stride=1, downsample=None, act=True):
24
+ super(DepthSeperabelConv2d, self).__init__()
25
+ self.depthwise = nn.Sequential(
26
+ nn.Conv2d(inplanes, inplanes, kernel_size, stride=stride, groups=inplanes, padding=kernel_size//2, bias=False),
27
+ nn.BatchNorm2d(inplanes, momentum=BN_MOMENTUM)
28
+ )
29
+ # self.depthwise = nn.Conv2d(inplanes, inplanes, kernel_size, stride=stride, groups=inplanes, padding=1, bias=False)
30
+ # self.pointwise = nn.Conv2d(inplanes, planes, 1, bias=False)
31
+
32
+ self.pointwise = nn.Sequential(
33
+ nn.Conv2d(inplanes, planes, 1, bias=False),
34
+ nn.BatchNorm2d(planes, momentum=BN_MOMENTUM)
35
+ )
36
+ self.downsample = downsample
37
+ self.stride = stride
38
+ try:
39
+ self.act = Hardswish() if act else nn.Identity()
40
+ except:
41
+ self.act = nn.Identity()
42
+
43
+ def forward(self, x):
44
+ #residual = x
45
+
46
+ out = self.depthwise(x)
47
+ out = self.act(out)
48
+ out = self.pointwise(out)
49
+
50
+ if self.downsample is not None:
51
+ residual = self.downsample(x)
52
+ out = self.act(out)
53
+
54
+ return out
55
+
56
+
57
+
58
+ class SharpenConv(nn.Module):
59
+ # SharpenConv convolution
60
+ def __init__(self, c1, c2, k=3, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
61
+ super(SharpenConv, self).__init__()
62
+ sobel_kernel = np.array([[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]], dtype='float32')
63
+ kenel_weight = np.vstack([sobel_kernel]*c2*c1).reshape(c2,c1,3,3)
64
+ self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
65
+ self.conv.weight.data = torch.from_numpy(kenel_weight)
66
+ self.conv.weight.requires_grad = False
67
+ self.bn = nn.BatchNorm2d(c2)
68
+ try:
69
+ self.act = Hardswish() if act else nn.Identity()
70
+ except:
71
+ self.act = nn.Identity()
72
+
73
+ def forward(self, x):
74
+ return self.act(self.bn(self.conv(x)))
75
+
76
+ def fuseforward(self, x):
77
+ return self.act(self.conv(x))
78
+
79
+
80
+ class Hardswish(nn.Module): # export-friendly version of nn.Hardswish()
81
+ @staticmethod
82
+ def forward(x):
83
+ # return x * F.hardsigmoid(x) # for torchscript and CoreML
84
+ return x * F.hardtanh(x + 3, 0., 6.) / 6. # for torchscript, CoreML and ONNX
85
+
86
+
87
+ class Conv(nn.Module):
88
+ # Standard convolution
89
+ def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
90
+ super(Conv, self).__init__()
91
+ self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
92
+ self.bn = nn.BatchNorm2d(c2)
93
+ try:
94
+ self.act = Hardswish() if act else nn.Identity()
95
+ except:
96
+ self.act = nn.Identity()
97
+
98
+ def forward(self, x):
99
+ return self.act(self.bn(self.conv(x)))
100
+
101
+ def fuseforward(self, x):
102
+ return self.act(self.conv(x))
103
+
104
+
105
+ class Bottleneck(nn.Module):
106
+ # Standard bottleneck
107
+ def __init__(self, c1, c2, shortcut=True, g=1, e=0.5): # ch_in, ch_out, shortcut, groups, expansion
108
+ super(Bottleneck, self).__init__()
109
+ c_ = int(c2 * e) # hidden channels
110
+ self.cv1 = Conv(c1, c_, 1, 1)
111
+ self.cv2 = Conv(c_, c2, 3, 1, g=g)
112
+ self.add = shortcut and c1 == c2
113
+
114
+ def forward(self, x):
115
+ return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))
116
+
117
+
118
+ class BottleneckCSP(nn.Module):
119
+ # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks
120
+ def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion
121
+ super(BottleneckCSP, self).__init__()
122
+ c_ = int(c2 * e) # hidden channels
123
+ self.cv1 = Conv(c1, c_, 1, 1)
124
+ self.cv2 = nn.Conv2d(c1, c_, 1, 1, bias=False)
125
+ self.cv3 = nn.Conv2d(c_, c_, 1, 1, bias=False)
126
+ self.cv4 = Conv(2 * c_, c2, 1, 1)
127
+ self.bn = nn.BatchNorm2d(2 * c_) # applied to cat(cv2, cv3)
128
+ self.act = nn.LeakyReLU(0.1, inplace=True)
129
+ self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)])
130
+
131
+ def forward(self, x):
132
+ y1 = self.cv3(self.m(self.cv1(x)))
133
+ y2 = self.cv2(x)
134
+ return self.cv4(self.act(self.bn(torch.cat((y1, y2), dim=1))))
135
+
136
+
137
+ class SPP(nn.Module):
138
+ # Spatial pyramid pooling layer used in YOLOv3-SPP
139
+ def __init__(self, c1, c2, k=(5, 9, 13)):
140
+ super(SPP, self).__init__()
141
+ c_ = c1 // 2 # hidden channels
142
+ self.cv1 = Conv(c1, c_, 1, 1)
143
+ self.cv2 = Conv(c_ * (len(k) + 1), c2, 1, 1)
144
+ self.m = nn.ModuleList([nn.MaxPool2d(kernel_size=x, stride=1, padding=x // 2) for x in k])
145
+
146
+ def forward(self, x):
147
+ x = self.cv1(x)
148
+ return self.cv2(torch.cat([x] + [m(x) for m in self.m], 1))
149
+
150
+
151
+ class Focus(nn.Module):
152
+ # Focus wh information into c-space
153
+ # slice concat conv
154
+ def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
155
+ super(Focus, self).__init__()
156
+ self.conv = Conv(c1 * 4, c2, k, s, p, g, act)
157
+
158
+ def forward(self, x): # x(b,c,w,h) -> y(b,4c,w/2,h/2)
159
+ return self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1))
160
+
161
+
162
+ class Concat(nn.Module):
163
+ # Concatenate a list of tensors along dimension
164
+ def __init__(self, dimension=1):
165
+ super(Concat, self).__init__()
166
+ self.d = dimension
167
+
168
+ def forward(self, x):
169
+ """ print("***********************")
170
+ for f in x:
171
+ print(f.shape) """
172
+ return torch.cat(x, self.d)
173
+
174
+
175
+ class Detect(nn.Module):
176
+ stride = None # strides computed during build
177
+
178
+ def __init__(self, nc=13, anchors=(), ch=()): # detection layer
179
+ super(Detect, self).__init__()
180
+ self.nc = nc # number of classes
181
+ self.no = nc + 5 # number of outputs per anchor 85
182
+ self.nl = len(anchors) # number of detection layers 3
183
+ self.na = len(anchors[0]) // 2 # number of anchors 3
184
+ self.grid = [torch.zeros(1)] * self.nl # init grid
185
+ a = torch.tensor(anchors).float().view(self.nl, -1, 2)
186
+ self.register_buffer('anchors', a) # shape(nl,na,2)
187
+ self.register_buffer('anchor_grid', a.clone().view(self.nl, 1, -1, 1, 1, 2)) # shape(nl,1,na,1,1,2)
188
+ self.m = nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch) # output conv
189
+
190
+ def forward(self, x):
191
+ z = [] # inference output
192
+ for i in range(self.nl):
193
+ x[i] = self.m[i](x[i]) # conv
194
+ # print(str(i)+str(x[i].shape))
195
+ bs, _, ny, nx = x[i].shape # x(bs,255,w,w) to x(bs,3,w,w,85)
196
+ x[i]=x[i].view(bs, self.na, self.no, ny*nx).permute(0, 1, 3, 2).view(bs, self.na, ny, nx, self.no).contiguous()
197
+ # x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous()
198
+ # print(str(i)+str(x[i].shape))
199
+
200
+ if not self.training: # inference
201
+ if self.grid[i].shape[2:4] != x[i].shape[2:4]:
202
+ self.grid[i] = self._make_grid(nx, ny).to(x[i].device)
203
+ y = x[i].sigmoid()
204
+ #print("**")
205
+ #print(y.shape) #[1, 3, w, h, 85]
206
+ #print(self.grid[i].shape) #[1, 3, w, h, 2]
207
+ y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i].to(x[i].device)) * self.stride[i] # xy
208
+ y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh
209
+ """print("**")
210
+ print(y.shape) #[1, 3, w, h, 85]
211
+ print(y.view(bs, -1, self.no).shape) #[1, 3*w*h, 85]"""
212
+ z.append(y.view(bs, -1, self.no))
213
+ return x if self.training else (torch.cat(z, 1), x)
214
+
215
+ @staticmethod
216
+ def _make_grid(nx=20, ny=20):
217
+
218
+ yv, xv = torch.meshgrid([torch.arange(ny), torch.arange(nx)])
219
+ return torch.stack((xv, yv), 2).view((1, 1, ny, nx, 2)).float()
220
+
221
+
222
+ """class Detections:
223
+ # detections class for YOLOv5 inference results
224
+ def __init__(self, imgs, pred, names=None):
225
+ super(Detections, self).__init__()
226
+ d = pred[0].device # device
227
+ gn = [torch.tensor([*[im.shape[i] for i in [1, 0, 1, 0]], 1., 1.], device=d) for im in imgs] # normalizations
228
+ self.imgs = imgs # list of images as numpy arrays
229
+ self.pred = pred # list of tensors pred[0] = (xyxy, conf, cls)
230
+ self.names = names # class names
231
+ self.xyxy = pred # xyxy pixels
232
+ self.xywh = [xyxy2xywh(x) for x in pred] # xywh pixels
233
+ self.xyxyn = [x / g for x, g in zip(self.xyxy, gn)] # xyxy normalized
234
+ self.xywhn = [x / g for x, g in zip(self.xywh, gn)] # xywh normalized
235
+ self.n = len(self.pred)
236
+ def display(self, pprint=False, show=False, save=False):
237
+ colors = color_list()
238
+ for i, (img, pred) in enumerate(zip(self.imgs, self.pred)):
239
+ str = f'Image {i + 1}/{len(self.pred)}: {img.shape[0]}x{img.shape[1]} '
240
+ if pred is not None:
241
+ for c in pred[:, -1].unique():
242
+ n = (pred[:, -1] == c).sum() # detections per class
243
+ str += f'{n} {self.names[int(c)]}s, ' # add to string
244
+ if show or save:
245
+ img = Image.fromarray(img.astype(np.uint8)) if isinstance(img, np.ndarray) else img # from np
246
+ for *box, conf, cls in pred: # xyxy, confidence, class
247
+ # str += '%s %.2f, ' % (names[int(cls)], conf) # label
248
+ ImageDraw.Draw(img).rectangle(box, width=4, outline=colors[int(cls) % 10]) # plot
249
+ if save:
250
+ f = f'results{i}.jpg'
251
+ str += f"saved to '{f}'"
252
+ img.save(f) # save
253
+ if show:
254
+ img.show(f'Image {i}') # show
255
+ if pprint:
256
+ print(str)
257
+ def print(self):
258
+ self.display(pprint=True) # print results
259
+ def show(self):
260
+ self.display(show=True) # show results
261
+ def save(self):
262
+ self.display(save=True) # save results
263
+ def __len__(self):
264
+ return self.n
265
+ def tolist(self):
266
+ # return a list of Detections objects, i.e. 'for result in results.tolist():'
267
+ x = [Detections([self.imgs[i]], [self.pred[i]], self.names) for i in range(self.n)]
268
+ for d in x:
269
+ for k in ['imgs', 'pred', 'xyxy', 'xyxyn', 'xywh', 'xywhn']:
270
+ setattr(d, k, getattr(d, k)[0]) # pop out of list"""
271
+
272
+