common.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. # This file contains modules common to various models
  2. import math
  3. import numpy as np
  4. import requests
  5. import torch
  6. import torch.nn as nn
  7. from PIL import Image, ImageDraw
  8. from utils.datasets import letterbox
  9. from utils.general import non_max_suppression, make_divisible, scale_coords, xyxy2xywh
  10. from utils.plots import color_list
  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. def channel_shuffle(x, groups):
  17. batchsize, num_channels, height, width = x.data.size()
  18. channels_per_group = num_channels // groups
  19. # reshape
  20. x = x.view(batchsize, groups, channels_per_group, height, width)
  21. x = torch.transpose(x, 1, 2).contiguous()
  22. # flatten
  23. x = x.view(batchsize, -1, height, width)
  24. return x
  25. def DWConv(c1, c2, k=1, s=1, act=True):
  26. # Depthwise convolution
  27. return Conv(c1, c2, k, s, g=math.gcd(c1, c2), act=act)
  28. class Conv(nn.Module):
  29. # Standard convolution
  30. def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
  31. super(Conv, self).__init__()
  32. self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
  33. self.bn = nn.BatchNorm2d(c2)
  34. self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
  35. #self.act = self.act = nn.LeakyReLU(0.1, inplace=True) if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
  36. def forward(self, x):
  37. return self.act(self.bn(self.conv(x)))
  38. def fuseforward(self, x):
  39. return self.act(self.conv(x))
  40. class StemBlock(nn.Module):
  41. def __init__(self, c1, c2, k=3, s=2, p=None, g=1, act=True):
  42. super(StemBlock, self).__init__()
  43. self.stem_1 = Conv(c1, c2, k, s, p, g, act)
  44. self.stem_2a = Conv(c2, c2 // 2, 1, 1, 0)
  45. self.stem_2b = Conv(c2 // 2, c2, 3, 2, 1)
  46. self.stem_2p = nn.MaxPool2d(kernel_size=2,stride=2,ceil_mode=True)
  47. self.stem_3 = Conv(c2 * 2, c2, 1, 1, 0)
  48. def forward(self, x):
  49. stem_1_out = self.stem_1(x)
  50. stem_2a_out = self.stem_2a(stem_1_out)
  51. stem_2b_out = self.stem_2b(stem_2a_out)
  52. stem_2p_out = self.stem_2p(stem_1_out)
  53. out = self.stem_3(torch.cat((stem_2b_out,stem_2p_out),1))
  54. return out
  55. class Bottleneck(nn.Module):
  56. # Standard bottleneck
  57. def __init__(self, c1, c2, shortcut=True, g=1, e=0.5): # ch_in, ch_out, shortcut, groups, expansion
  58. super(Bottleneck, self).__init__()
  59. c_ = int(c2 * e) # hidden channels
  60. self.cv1 = Conv(c1, c_, 1, 1)
  61. self.cv2 = Conv(c_, c2, 3, 1, g=g)
  62. self.add = shortcut and c1 == c2
  63. def forward(self, x):
  64. return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))
  65. class BottleneckCSP(nn.Module):
  66. # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks
  67. def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion
  68. super(BottleneckCSP, self).__init__()
  69. c_ = int(c2 * e) # hidden channels
  70. self.cv1 = Conv(c1, c_, 1, 1)
  71. self.cv2 = nn.Conv2d(c1, c_, 1, 1, bias=False)
  72. self.cv3 = nn.Conv2d(c_, c_, 1, 1, bias=False)
  73. self.cv4 = Conv(2 * c_, c2, 1, 1)
  74. self.bn = nn.BatchNorm2d(2 * c_) # applied to cat(cv2, cv3)
  75. self.act = nn.LeakyReLU(0.1, inplace=True)
  76. self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)])
  77. def forward(self, x):
  78. y1 = self.cv3(self.m(self.cv1(x)))
  79. y2 = self.cv2(x)
  80. return self.cv4(self.act(self.bn(torch.cat((y1, y2), dim=1))))
  81. class C3(nn.Module):
  82. # CSP Bottleneck with 3 convolutions
  83. def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion
  84. super(C3, self).__init__()
  85. c_ = int(c2 * e) # hidden channels
  86. self.cv1 = Conv(c1, c_, 1, 1)
  87. self.cv2 = Conv(c1, c_, 1, 1)
  88. self.cv3 = Conv(2 * c_, c2, 1) # act=FReLU(c2)
  89. self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)])
  90. def forward(self, x):
  91. return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), dim=1))
  92. class ShuffleV2Block(nn.Module):
  93. def __init__(self, inp, oup, stride):
  94. super(ShuffleV2Block, self).__init__()
  95. if not (1 <= stride <= 3):
  96. raise ValueError('illegal stride value')
  97. self.stride = stride
  98. branch_features = oup // 2
  99. assert (self.stride != 1) or (inp == branch_features << 1)
  100. if self.stride > 1:
  101. self.branch1 = nn.Sequential(
  102. self.depthwise_conv(inp, inp, kernel_size=3, stride=self.stride, padding=1),
  103. nn.BatchNorm2d(inp),
  104. nn.Conv2d(inp, branch_features, kernel_size=1, stride=1, padding=0, bias=False),
  105. nn.BatchNorm2d(branch_features),
  106. nn.SiLU(),
  107. )
  108. else:
  109. self.branch1 = nn.Sequential()
  110. self.branch2 = nn.Sequential(
  111. nn.Conv2d(inp if (self.stride > 1) else branch_features, branch_features, kernel_size=1, stride=1, padding=0, bias=False),
  112. nn.BatchNorm2d(branch_features),
  113. nn.SiLU(),
  114. self.depthwise_conv(branch_features, branch_features, kernel_size=3, stride=self.stride, padding=1),
  115. nn.BatchNorm2d(branch_features),
  116. nn.Conv2d(branch_features, branch_features, kernel_size=1, stride=1, padding=0, bias=False),
  117. nn.BatchNorm2d(branch_features),
  118. nn.SiLU(),
  119. )
  120. @staticmethod
  121. def depthwise_conv(i, o, kernel_size, stride=1, padding=0, bias=False):
  122. return nn.Conv2d(i, o, kernel_size, stride, padding, bias=bias, groups=i)
  123. def forward(self, x):
  124. if self.stride == 1:
  125. x1, x2 = x.chunk(2, dim=1)
  126. out = torch.cat((x1, self.branch2(x2)), dim=1)
  127. else:
  128. out = torch.cat((self.branch1(x), self.branch2(x)), dim=1)
  129. out = channel_shuffle(out, 2)
  130. return out
  131. class BlazeBlock(nn.Module):
  132. def __init__(self, in_channels,out_channels,mid_channels=None,stride=1):
  133. super(BlazeBlock, self).__init__()
  134. mid_channels = mid_channels or in_channels
  135. assert stride in [1, 2]
  136. if stride>1:
  137. self.use_pool = True
  138. else:
  139. self.use_pool = False
  140. self.branch1 = nn.Sequential(
  141. nn.Conv2d(in_channels=in_channels,out_channels=mid_channels,kernel_size=5,stride=stride,padding=2,groups=in_channels),
  142. nn.BatchNorm2d(mid_channels),
  143. nn.Conv2d(in_channels=mid_channels,out_channels=out_channels,kernel_size=1,stride=1),
  144. nn.BatchNorm2d(out_channels),
  145. )
  146. if self.use_pool:
  147. self.shortcut = nn.Sequential(
  148. nn.MaxPool2d(kernel_size=stride, stride=stride),
  149. nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=1, stride=1),
  150. nn.BatchNorm2d(out_channels),
  151. )
  152. self.relu = nn.SiLU(inplace=True)
  153. def forward(self, x):
  154. branch1 = self.branch1(x)
  155. out = (branch1+self.shortcut(x)) if self.use_pool else (branch1+x)
  156. return self.relu(out)
  157. class DoubleBlazeBlock(nn.Module):
  158. def __init__(self,in_channels,out_channels,mid_channels=None,stride=1):
  159. super(DoubleBlazeBlock, self).__init__()
  160. mid_channels = mid_channels or in_channels
  161. assert stride in [1, 2]
  162. if stride > 1:
  163. self.use_pool = True
  164. else:
  165. self.use_pool = False
  166. self.branch1 = nn.Sequential(
  167. nn.Conv2d(in_channels=in_channels, out_channels=in_channels, kernel_size=5, stride=stride,padding=2,groups=in_channels),
  168. nn.BatchNorm2d(in_channels),
  169. nn.Conv2d(in_channels=in_channels, out_channels=mid_channels, kernel_size=1, stride=1),
  170. nn.BatchNorm2d(mid_channels),
  171. nn.SiLU(inplace=True),
  172. nn.Conv2d(in_channels=mid_channels, out_channels=mid_channels, kernel_size=5, stride=1,padding=2),
  173. nn.BatchNorm2d(mid_channels),
  174. nn.Conv2d(in_channels=mid_channels, out_channels=out_channels, kernel_size=1, stride=1),
  175. nn.BatchNorm2d(out_channels),
  176. )
  177. if self.use_pool:
  178. self.shortcut = nn.Sequential(
  179. nn.MaxPool2d(kernel_size=stride, stride=stride),
  180. nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=1, stride=1),
  181. nn.BatchNorm2d(out_channels),
  182. )
  183. self.relu = nn.SiLU(inplace=True)
  184. def forward(self, x):
  185. branch1 = self.branch1(x)
  186. out = (branch1 + self.shortcut(x)) if self.use_pool else (branch1 + x)
  187. return self.relu(out)
  188. class SPP(nn.Module):
  189. # Spatial pyramid pooling layer used in YOLOv3-SPP
  190. def __init__(self, c1, c2, k=(5, 9, 13)):
  191. super(SPP, self).__init__()
  192. c_ = c1 // 2 # hidden channels
  193. self.cv1 = Conv(c1, c_, 1, 1)
  194. self.cv2 = Conv(c_ * (len(k) + 1), c2, 1, 1)
  195. self.m = nn.ModuleList([nn.MaxPool2d(kernel_size=x, stride=1, padding=x // 2) for x in k])
  196. def forward(self, x):
  197. x = self.cv1(x)
  198. return self.cv2(torch.cat([x] + [m(x) for m in self.m], 1))
  199. class SPPF(nn.Module):
  200. # Spatial Pyramid Pooling - Fast (SPPF) layer for YOLOv5 by Glenn Jocher
  201. def __init__(self, c1, c2, k=5): # equivalent to SPP(k=(5, 9, 13))
  202. super().__init__()
  203. c_ = c1 // 2 # hidden channels
  204. self.cv1 = Conv(c1, c_, 1, 1)
  205. self.cv2 = Conv(c_ * 4, c2, 1, 1)
  206. self.m = nn.MaxPool2d(kernel_size=k, stride=1, padding=k // 2)
  207. def forward(self, x):
  208. x = self.cv1(x)
  209. with warnings.catch_warnings():
  210. warnings.simplefilter('ignore') # suppress torch 1.9.0 max_pool2d() warning
  211. y1 = self.m(x)
  212. y2 = self.m(y1)
  213. return self.cv2(torch.cat((x, y1, y2, self.m(y2)), 1))
  214. class Focus(nn.Module):
  215. # Focus wh information into c-space
  216. def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
  217. super(Focus, self).__init__()
  218. self.conv = Conv(c1 * 4, c2, k, s, p, g, act)
  219. # self.contract = Contract(gain=2)
  220. def forward(self, x): # x(b,c,w,h) -> y(b,4c,w/2,h/2)
  221. return self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1))
  222. # return self.conv(self.contract(x))
  223. class Contract(nn.Module):
  224. # Contract width-height into channels, i.e. x(1,64,80,80) to x(1,256,40,40)
  225. def __init__(self, gain=2):
  226. super().__init__()
  227. self.gain = gain
  228. def forward(self, x):
  229. N, C, H, W = x.size() # assert (H / s == 0) and (W / s == 0), 'Indivisible gain'
  230. s = self.gain
  231. x = x.view(N, C, H // s, s, W // s, s) # x(1,64,40,2,40,2)
  232. x = x.permute(0, 3, 5, 1, 2, 4).contiguous() # x(1,2,2,64,40,40)
  233. return x.view(N, C * s * s, H // s, W // s) # x(1,256,40,40)
  234. class Expand(nn.Module):
  235. # Expand channels into width-height, i.e. x(1,64,80,80) to x(1,16,160,160)
  236. def __init__(self, gain=2):
  237. super().__init__()
  238. self.gain = gain
  239. def forward(self, x):
  240. N, C, H, W = x.size() # assert C / s ** 2 == 0, 'Indivisible gain'
  241. s = self.gain
  242. x = x.view(N, s, s, C // s ** 2, H, W) # x(1,2,2,16,80,80)
  243. x = x.permute(0, 3, 4, 1, 5, 2).contiguous() # x(1,16,80,2,80,2)
  244. return x.view(N, C // s ** 2, H * s, W * s) # x(1,16,160,160)
  245. class Concat(nn.Module):
  246. # Concatenate a list of tensors along dimension
  247. def __init__(self, dimension=1):
  248. super(Concat, self).__init__()
  249. self.d = dimension
  250. def forward(self, x):
  251. return torch.cat(x, self.d)
  252. class NMS(nn.Module):
  253. # Non-Maximum Suppression (NMS) module
  254. conf = 0.25 # confidence threshold
  255. iou = 0.45 # IoU threshold
  256. classes = None # (optional list) filter by class
  257. def __init__(self):
  258. super(NMS, self).__init__()
  259. def forward(self, x):
  260. return non_max_suppression(x[0], conf_thres=self.conf, iou_thres=self.iou, classes=self.classes)
  261. class autoShape(nn.Module):
  262. # input-robust model wrapper for passing cv2/np/PIL/torch inputs. Includes preprocessing, inference and NMS
  263. img_size = 640 # inference size (pixels)
  264. conf = 0.25 # NMS confidence threshold
  265. iou = 0.45 # NMS IoU threshold
  266. classes = None # (optional list) filter by class
  267. def __init__(self, model):
  268. super(autoShape, self).__init__()
  269. self.model = model.eval()
  270. def autoshape(self):
  271. print('autoShape already enabled, skipping... ') # model already converted to model.autoshape()
  272. return self
  273. def forward(self, imgs, size=640, augment=False, profile=False):
  274. # Inference from various sources. For height=720, width=1280, RGB images example inputs are:
  275. # filename: imgs = 'data/samples/zidane.jpg'
  276. # URI: = 'https://github.com/ultralytics/yolov5/releases/download/v1.0/zidane.jpg'
  277. # OpenCV: = cv2.imread('image.jpg')[:,:,::-1] # HWC BGR to RGB x(720,1280,3)
  278. # PIL: = Image.open('image.jpg') # HWC x(720,1280,3)
  279. # numpy: = np.zeros((720,1280,3)) # HWC
  280. # torch: = torch.zeros(16,3,720,1280) # BCHW
  281. # multiple: = [Image.open('image1.jpg'), Image.open('image2.jpg'), ...] # list of images
  282. p = next(self.model.parameters()) # for device and type
  283. if isinstance(imgs, torch.Tensor): # torch
  284. return self.model(imgs.to(p.device).type_as(p), augment, profile) # inference
  285. # Pre-process
  286. n, imgs = (len(imgs), imgs) if isinstance(imgs, list) else (1, [imgs]) # number of images, list of images
  287. shape0, shape1 = [], [] # image and inference shapes
  288. for i, im in enumerate(imgs):
  289. if isinstance(im, str): # filename or uri
  290. im = Image.open(requests.get(im, stream=True).raw if im.startswith('http') else im) # open
  291. im = np.array(im) # to numpy
  292. if im.shape[0] < 5: # image in CHW
  293. im = im.transpose((1, 2, 0)) # reverse dataloader .transpose(2, 0, 1)
  294. im = im[:, :, :3] if im.ndim == 3 else np.tile(im[:, :, None], 3) # enforce 3ch input
  295. s = im.shape[:2] # HWC
  296. shape0.append(s) # image shape
  297. g = (size / max(s)) # gain
  298. shape1.append([y * g for y in s])
  299. imgs[i] = im # update
  300. shape1 = [make_divisible(x, int(self.stride.max())) for x in np.stack(shape1, 0).max(0)] # inference shape
  301. x = [letterbox(im, new_shape=shape1, auto=False)[0] for im in imgs] # pad
  302. x = np.stack(x, 0) if n > 1 else x[0][None] # stack
  303. x = np.ascontiguousarray(x.transpose((0, 3, 1, 2))) # BHWC to BCHW
  304. x = torch.from_numpy(x).to(p.device).type_as(p) / 255. # uint8 to fp16/32
  305. # Inference
  306. with torch.no_grad():
  307. y = self.model(x, augment, profile)[0] # forward
  308. y = non_max_suppression(y, conf_thres=self.conf, iou_thres=self.iou, classes=self.classes) # NMS
  309. # Post-process
  310. for i in range(n):
  311. scale_coords(shape1, y[i][:, :4], shape0[i])
  312. return Detections(imgs, y, self.names)
  313. class Detections:
  314. # detections class for YOLOv5 inference results
  315. def __init__(self, imgs, pred, names=None):
  316. super(Detections, self).__init__()
  317. d = pred[0].device # device
  318. gn = [torch.tensor([*[im.shape[i] for i in [1, 0, 1, 0]], 1., 1.], device=d) for im in imgs] # normalizations
  319. self.imgs = imgs # list of images as numpy arrays
  320. self.pred = pred # list of tensors pred[0] = (xyxy, conf, cls)
  321. self.names = names # class names
  322. self.xyxy = pred # xyxy pixels
  323. self.xywh = [xyxy2xywh(x) for x in pred] # xywh pixels
  324. self.xyxyn = [x / g for x, g in zip(self.xyxy, gn)] # xyxy normalized
  325. self.xywhn = [x / g for x, g in zip(self.xywh, gn)] # xywh normalized
  326. self.n = len(self.pred)
  327. def display(self, pprint=False, show=False, save=False, render=False):
  328. colors = color_list()
  329. for i, (img, pred) in enumerate(zip(self.imgs, self.pred)):
  330. str = f'Image {i + 1}/{len(self.pred)}: {img.shape[0]}x{img.shape[1]} '
  331. if pred is not None:
  332. for c in pred[:, -1].unique():
  333. n = (pred[:, -1] == c).sum() # detections per class
  334. str += f'{n} {self.names[int(c)]}s, ' # add to string
  335. if show or save or render:
  336. img = Image.fromarray(img.astype(np.uint8)) if isinstance(img, np.ndarray) else img # from np
  337. for *box, conf, cls in pred: # xyxy, confidence, class
  338. # str += '%s %.2f, ' % (names[int(cls)], conf) # label
  339. ImageDraw.Draw(img).rectangle(box, width=4, outline=colors[int(cls) % 10]) # plot
  340. if pprint:
  341. print(str)
  342. if show:
  343. img.show(f'Image {i}') # show
  344. if save:
  345. f = f'results{i}.jpg'
  346. str += f"saved to '{f}'"
  347. img.save(f) # save
  348. if render:
  349. self.imgs[i] = np.asarray(img)
  350. def print(self):
  351. self.display(pprint=True) # print results
  352. def show(self):
  353. self.display(show=True) # show results
  354. def save(self):
  355. self.display(save=True) # save results
  356. def render(self):
  357. self.display(render=True) # render results
  358. return self.imgs
  359. def __len__(self):
  360. return self.n
  361. def tolist(self):
  362. # return a list of Detections objects, i.e. 'for result in results.tolist():'
  363. x = [Detections([self.imgs[i]], [self.pred[i]], self.names) for i in range(self.n)]
  364. for d in x:
  365. for k in ['imgs', 'pred', 'xyxy', 'xyxyn', 'xywh', 'xywhn']:
  366. setattr(d, k, getattr(d, k)[0]) # pop out of list
  367. return x
  368. class Classify(nn.Module):
  369. # Classification head, i.e. x(b,c1,20,20) to x(b,c2)
  370. def __init__(self, c1, c2, k=1, s=1, p=None, g=1): # ch_in, ch_out, kernel, stride, padding, groups
  371. super(Classify, self).__init__()
  372. self.aap = nn.AdaptiveAvgPool2d(1) # to x(b,c1,1,1)
  373. self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g) # to x(b,c2,1,1)
  374. self.flat = nn.Flatten()
  375. def forward(self, x):
  376. z = torch.cat([self.aap(y) for y in (x if isinstance(x, list) else [x])], 1) # cat if list
  377. return self.flat(self.conv(z)) # flatten to x(b,c2)