TensorRT转化代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

пре 8 месеци
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. # YOLOv5 common modules
  2. import logging
  3. import warnings
  4. from copy import copy
  5. from pathlib import Path
  6. import math
  7. import numpy as np
  8. import pandas as pd
  9. import requests
  10. import torch
  11. import torch.nn as nn
  12. from PIL import Image
  13. from torch.cuda import amp
  14. from utils.datasets import exif_transpose, letterbox
  15. from utils.general import non_max_suppression, make_divisible, scale_coords, increment_path, xyxy2xywh, save_one_box
  16. from utils.plots import colors, plot_one_box
  17. from utils.torch_utils import time_sync
  18. from functools import partial
  19. LOGGER = logging.getLogger(__name__)
  20. def autopad(k, p=None): # kernel, padding
  21. # Pad to 'same'
  22. if p is None:
  23. p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad
  24. return p
  25. class Conv(nn.Module):
  26. # Standard convolution
  27. def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
  28. super().__init__()
  29. self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
  30. self.bn = nn.BatchNorm2d(c2)
  31. self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
  32. def forward(self, x):
  33. return self.act(self.bn(self.conv(x)))
  34. def forward_fuse(self, x):
  35. return self.act(self.conv(x))
  36. class DWConv(Conv):
  37. # Depth-wise convolution class if g = c1 = c2
  38. def __init__(self, c1, c2, k=1, s=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
  39. super().__init__(c1, c2, k, s, g=math.gcd(c1, c2), act=act)
  40. class TransformerLayer(nn.Module):
  41. # Transformer layer https://arxiv.org/abs/2010.11929 (LayerNorm layers removed for better performance)
  42. def __init__(self, c, num_heads):
  43. super().__init__()
  44. self.q = nn.Linear(c, c, bias=False)
  45. self.k = nn.Linear(c, c, bias=False)
  46. self.v = nn.Linear(c, c, bias=False)
  47. self.ma = nn.MultiheadAttention(embed_dim=c, num_heads=num_heads)
  48. self.fc1 = nn.Linear(c, c, bias=False)
  49. self.fc2 = nn.Linear(c, c, bias=False)
  50. def forward(self, x):
  51. x = self.ma(self.q(x), self.k(x), self.v(x))[0] + x
  52. x = self.fc2(self.fc1(x)) + x
  53. return x
  54. class TransformerBlock(nn.Module):
  55. # Vision Transformer https://arxiv.org/abs/2010.11929
  56. def __init__(self, c1, c2, num_heads, num_layers):
  57. super().__init__()
  58. self.conv = None
  59. if c1 != c2:
  60. self.conv = Conv(c1, c2)
  61. self.linear = nn.Linear(c2, c2) # learnable position embedding
  62. self.tr = nn.Sequential(*[TransformerLayer(c2, num_heads) for _ in range(num_layers)])
  63. self.c2 = c2
  64. def forward(self, x):
  65. if self.conv is not None:
  66. x = self.conv(x)
  67. b, _, w, h = x.shape
  68. p = x.flatten(2).unsqueeze(0).transpose(0, 3).squeeze(3)
  69. return self.tr(p + self.linear(p)).unsqueeze(3).transpose(0, 3).reshape(b, self.c2, w, h)
  70. class Bottleneck(nn.Module):
  71. # Res unit
  72. # Standard bottleneck
  73. def __init__(self, c1, c2, shortcut=True, g=1, e=0.5): # ch_in, ch_out, shortcut, groups, expansion
  74. super().__init__()
  75. c_ = int(c2 * e) # hidden channels
  76. self.cv1 = Conv(c1, c_, 1, 1)
  77. self.cv2 = Conv(c_, c2, 3, 1, g=g)
  78. self.add = shortcut and c1 == c2
  79. def forward(self, x):
  80. return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))
  81. class BottleneckCSP(nn.Module):
  82. #CSP1_x or CSP2_x
  83. # CSP Bottleneck https://github.com/WongKinYiu/CrossStagePartialNetworks
  84. def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion
  85. super().__init__()
  86. c_ = int(c2 * e) # hidden channels
  87. self.cv1 = Conv(c1, c_, 1, 1)
  88. self.cv2 = nn.Conv2d(c1, c_, 1, 1, bias=False)
  89. self.cv3 = nn.Conv2d(c_, c_, 1, 1, bias=False)
  90. self.cv4 = Conv(2 * c_, c2, 1, 1)
  91. self.bn = nn.BatchNorm2d(2 * c_) # applied to cat(cv2, cv3)
  92. self.act = nn.LeakyReLU(0.1, inplace=True)
  93. self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)])
  94. def forward(self, x):
  95. y1 = self.cv3(self.m(self.cv1(x)))
  96. y2 = self.cv2(x)
  97. return self.cv4(self.act(self.bn(torch.cat((y1, y2), dim=1))))
  98. class C3(nn.Module):
  99. # CSP Bottleneck with 3 convolutions
  100. def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, number, shortcut, groups, expansion
  101. super().__init__()
  102. c_ = int(c2 * e) # hidden channels
  103. self.cv1 = Conv(c1, c_, 1, 1)
  104. self.cv2 = Conv(c1, c_, 1, 1)
  105. self.cv3 = Conv(2 * c_, c2, 1) # act=FReLU(c2)
  106. self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)])
  107. # self.m = nn.Sequential(*[CrossConv(c_, c_, 3, 1, g, 1.0, shortcut) for _ in range(n)])
  108. def forward(self, x):
  109. return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), dim=1))
  110. class C3TR(C3):
  111. # C3 module with TransformerBlock()
  112. def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
  113. super().__init__(c1, c2, n, shortcut, g, e)
  114. c_ = int(c2 * e)
  115. self.m = TransformerBlock(c_, c_, 4, n)
  116. class C3SPP(C3):
  117. # C3 module with SPP()
  118. def __init__(self, c1, c2, k=(5, 9, 13), n=1, shortcut=True, g=1, e=0.5):
  119. super().__init__(c1, c2, n, shortcut, g, e)
  120. c_ = int(c2 * e)
  121. self.m = SPP(c_, c_, k)
  122. class C3Ghost(C3):
  123. # C3 module with GhostBottleneck()
  124. def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
  125. super().__init__(c1, c2, n, shortcut, g, e)
  126. c_ = int(c2 * e) # hidden channels
  127. self.m = nn.Sequential(*[GhostBottleneck(c_, c_) for _ in range(n)])
  128. class SPP(nn.Module):
  129. # Spatial Pyramid Pooling (SPP) layer https://arxiv.org/abs/1406.4729
  130. def __init__(self, c1, c2, k=(5, 9, 13)):
  131. super().__init__()
  132. c_ = c1 // 2 # hidden channels
  133. self.cv1 = Conv(c1, c_, 1, 1)
  134. self.cv2 = Conv(c_ * (len(k) + 1), c2, 1, 1)
  135. self.m = nn.ModuleList([nn.MaxPool2d(kernel_size=x, stride=1, padding=x // 2) for x in k])
  136. def forward(self, x):
  137. x = self.cv1(x)
  138. with warnings.catch_warnings():
  139. warnings.simplefilter('ignore') # suppress torch 1.9.0 max_pool2d() warning
  140. return self.cv2(torch.cat([x] + [m(x) for m in self.m], 1))
  141. class SPPF(nn.Module):
  142. # Spatial Pyramid Pooling - Fast (SPPF) layer for YOLOv5 by Glenn Jocher
  143. def __init__(self, c1, c2, k=5): # equivalent to SPP(k=(5, 9, 13))
  144. super().__init__()
  145. c_ = c1 // 2 # hidden channels
  146. self.cv1 = Conv(c1, c_, 1, 1)
  147. self.cv2 = Conv(c_ * 4, c2, 1, 1)
  148. self.m = nn.MaxPool2d(kernel_size=k, stride=1, padding=k // 2)
  149. def forward(self, x):
  150. x = self.cv1(x)
  151. with warnings.catch_warnings():
  152. warnings.simplefilter('ignore') # suppress torch 1.9.0 max_pool2d() warning
  153. y1 = self.m(x)
  154. y2 = self.m(y1)
  155. return self.cv2(torch.cat([x, y1, y2, self.m(y2)], 1))
  156. class Focus(nn.Module):
  157. # Focus wh information into c-space
  158. def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True): # ch_in, ch_out, kernel, stride, padding, groups
  159. super().__init__()
  160. self.conv = Conv(c1 * 4, c2, k, s, p, g, act)
  161. # self.contract = Contract(gain=2)
  162. def forward(self, x): # x(b,c,w,h) -> y(b,4c,w/2,h/2)
  163. return self.conv(torch.cat([x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]], 1))
  164. # return self.conv(self.contract(x))
  165. class GhostConv(nn.Module):
  166. # Ghost Convolution https://github.com/huawei-noah/ghostnet
  167. def __init__(self, c1, c2, k=1, s=1, g=1, act=True): # ch_in, ch_out, kernel, stride, groups
  168. super().__init__()
  169. c_ = c2 // 2 # hidden channels
  170. self.cv1 = Conv(c1, c_, k, s, None, g, act)
  171. self.cv2 = Conv(c_, c_, 5, 1, None, c_, act)
  172. def forward(self, x):
  173. y = self.cv1(x)
  174. return torch.cat([y, self.cv2(y)], 1)
  175. class GhostBottleneck(nn.Module):
  176. # Ghost Bottleneck https://github.com/huawei-noah/ghostnet
  177. def __init__(self, c1, c2, k=3, s=1): # ch_in, ch_out, kernel, stride
  178. super().__init__()
  179. c_ = c2 // 2
  180. self.conv = nn.Sequential(GhostConv(c1, c_, 1, 1), # pw
  181. DWConv(c_, c_, k, s, act=False) if s == 2 else nn.Identity(), # dw
  182. GhostConv(c_, c2, 1, 1, act=False)) # pw-linear
  183. self.shortcut = nn.Sequential(DWConv(c1, c1, k, s, act=False),
  184. Conv(c1, c2, 1, 1, act=False)) if s == 2 else nn.Identity()
  185. def forward(self, x):
  186. return self.conv(x) + self.shortcut(x)
  187. class Contract(nn.Module):
  188. # Contract width-height into channels, i.e. x(1,64,80,80) to x(1,256,40,40)
  189. def __init__(self, gain=2):
  190. super().__init__()
  191. self.gain = gain
  192. def forward(self, x):
  193. b, c, h, w = x.size() # assert (h / s == 0) and (W / s == 0), 'Indivisible gain'
  194. s = self.gain
  195. x = x.view(b, c, h // s, s, w // s, s) # x(1,64,40,2,40,2)
  196. x = x.permute(0, 3, 5, 1, 2, 4).contiguous() # x(1,2,2,64,40,40)
  197. return x.view(b, c * s * s, h // s, w // s) # x(1,256,40,40)
  198. class Expand(nn.Module):
  199. # Expand channels into width-height, i.e. x(1,64,80,80) to x(1,16,160,160)
  200. def __init__(self, gain=2):
  201. super().__init__()
  202. self.gain = gain
  203. def forward(self, x):
  204. b, c, h, w = x.size() # assert C / s ** 2 == 0, 'Indivisible gain'
  205. s = self.gain
  206. x = x.view(b, s, s, c // s ** 2, h, w) # x(1,2,2,16,80,80)
  207. x = x.permute(0, 3, 4, 1, 5, 2).contiguous() # x(1,16,80,2,80,2)
  208. return x.view(b, c // s ** 2, h * s, w * s) # x(1,16,160,160)
  209. class Concat(nn.Module):
  210. # Concatenate a list of tensors along dimension
  211. def __init__(self, dimension=1):
  212. super().__init__()
  213. self.d = dimension
  214. def forward(self, x):
  215. return torch.cat(x, self.d)
  216. class AutoShape(nn.Module):
  217. # YOLOv5 input-robust model wrapper for passing cv2/np/PIL/torch inputs. Includes preprocessing, inference and NMS
  218. conf = 0.25 # NMS confidence threshold
  219. iou = 0.45 # NMS IoU threshold
  220. classes = None # (optional list) filter by class
  221. max_det = 1000 # maximum number of detections per image
  222. def __init__(self, model):
  223. super().__init__()
  224. self.model = model.eval()
  225. def autoshape(self):
  226. LOGGER.info('AutoShape already enabled, skipping... ') # model already converted to model.autoshape()
  227. return self
  228. @torch.no_grad()
  229. def forward(self, imgs, size=640, augment=False, profile=False):
  230. # Inference from various sources. For height=640, width=1280, RGB images example inputs are:
  231. # file: imgs = 'data/images/zidane.jpg' # str or PosixPath
  232. # URI: = 'https://ultralytics.com/images/zidane.jpg'
  233. # OpenCV: = cv2.imread('image.jpg')[:,:,::-1] # HWC BGR to RGB x(640,1280,3)
  234. # PIL: = Image.open('image.jpg') or ImageGrab.grab() # HWC x(640,1280,3)
  235. # numpy: = np.zeros((640,1280,3)) # HWC
  236. # torch: = torch.zeros(16,3,320,640) # BCHW (scaled to size=640, 0-1 values)
  237. # multiple: = [Image.open('image1.jpg'), Image.open('image2.jpg'), ...] # list of images
  238. t = [time_sync()]
  239. p = next(self.model.parameters()) # for device and type
  240. if isinstance(imgs, torch.Tensor): # torch
  241. with amp.autocast(enabled=p.device.type != 'cpu'):
  242. return self.model(imgs.to(p.device).type_as(p), augment, profile) # inference
  243. # Pre-process
  244. n, imgs = (len(imgs), imgs) if isinstance(imgs, list) else (1, [imgs]) # number of images, list of images
  245. shape0, shape1, files = [], [], [] # image and inference shapes, filenames
  246. for i, im in enumerate(imgs):
  247. f = f'image{i}' # filename
  248. if isinstance(im, (str, Path)): # filename or uri
  249. im, f = Image.open(requests.get(im, stream=True).raw if str(im).startswith('http') else im), im
  250. im = np.asarray(exif_transpose(im))
  251. elif isinstance(im, Image.Image): # PIL Image
  252. im, f = np.asarray(exif_transpose(im)), getattr(im, 'filename', f) or f
  253. files.append(Path(f).with_suffix('.jpg').name)
  254. if im.shape[0] < 5: # image in CHW
  255. im = im.transpose((1, 2, 0)) # reverse dataloader .transpose(2, 0, 1)
  256. im = im[..., :3] if im.ndim == 3 else np.tile(im[..., None], 3) # enforce 3ch input
  257. s = im.shape[:2] # HWC
  258. shape0.append(s) # image shape
  259. g = (size / max(s)) # gain
  260. shape1.append([y * g for y in s])
  261. imgs[i] = im if im.data.contiguous else np.ascontiguousarray(im) # update
  262. shape1 = [make_divisible(x, int(self.stride.max())) for x in np.stack(shape1, 0).max(0)] # inference shape
  263. x = [letterbox(im, new_shape=shape1, auto=False)[0] for im in imgs] # pad
  264. x = np.stack(x, 0) if n > 1 else x[0][None] # stack
  265. x = np.ascontiguousarray(x.transpose((0, 3, 1, 2))) # BHWC to BCHW
  266. x = torch.from_numpy(x).to(p.device).type_as(p) / 255. # uint8 to fp16/32
  267. t.append(time_sync())
  268. with amp.autocast(enabled=p.device.type != 'cpu'):
  269. # Inference
  270. y = self.model(x, augment, profile)[0] # forward
  271. t.append(time_sync())
  272. # Post-process
  273. y = non_max_suppression(y, self.conf, iou_thres=self.iou, classes=self.classes, max_det=self.max_det) # NMS
  274. for i in range(n):
  275. scale_coords(shape1, y[i][:, :4], shape0[i])
  276. t.append(time_sync())
  277. return Detections(imgs, y, files, t, self.names, x.shape)
  278. class Detections:
  279. # YOLOv5 detections class for inference results
  280. def __init__(self, imgs, pred, files, times=None, names=None, shape=None):
  281. super().__init__()
  282. d = pred[0].device # device
  283. gn = [torch.tensor([*[im.shape[i] for i in [1, 0, 1, 0]], 1., 1.], device=d) for im in imgs] # normalizations
  284. self.imgs = imgs # list of images as numpy arrays
  285. self.pred = pred # list of tensors pred[0] = (xyxy, conf, cls)
  286. self.names = names # class names
  287. self.files = files # image filenames
  288. self.xyxy = pred # xyxy pixels
  289. self.xywh = [xyxy2xywh(x) for x in pred] # xywh pixels
  290. self.xyxyn = [x / g for x, g in zip(self.xyxy, gn)] # xyxy normalized
  291. self.xywhn = [x / g for x, g in zip(self.xywh, gn)] # xywh normalized
  292. self.n = len(self.pred) # number of images (batch size)
  293. self.t = tuple((times[i + 1] - times[i]) * 1000 / self.n for i in range(3)) # timestamps (ms)
  294. self.s = shape # inference BCHW shape
  295. def display(self, pprint=False, show=False, save=False, crop=False, render=False, save_dir=Path('')):
  296. for i, (im, pred) in enumerate(zip(self.imgs, self.pred)):
  297. str = f'image {i + 1}/{len(self.pred)}: {im.shape[0]}x{im.shape[1]} '
  298. if pred.shape[0]:
  299. for c in pred[:, -1].unique():
  300. n = (pred[:, -1] == c).sum() # detections per class
  301. str += f"{n} {self.names[int(c)]}{'s' * (n > 1)}, " # add to string
  302. if show or save or render or crop:
  303. for *box, conf, cls in reversed(pred): # xyxy, confidence, class
  304. label = f'{self.names[int(cls)]} {conf:.2f}'
  305. if crop:
  306. save_one_box(box, im, file=save_dir / 'crops' / self.names[int(cls)] / self.files[i])
  307. else: # all others
  308. plot_one_box(box, im, label=label, color=colors(cls))
  309. else:
  310. str += '(no detections)'
  311. im = Image.fromarray(im.astype(np.uint8)) if isinstance(im, np.ndarray) else im # from np
  312. if pprint:
  313. LOGGER.info(str.rstrip(', '))
  314. if show:
  315. im.show(self.files[i]) # show
  316. if save:
  317. f = self.files[i]
  318. im.save(save_dir / f) # save
  319. if i == self.n - 1:
  320. LOGGER.info(f"Saved {self.n} image{'s' * (self.n > 1)} to '{save_dir}'")
  321. if render:
  322. self.imgs[i] = np.asarray(im)
  323. def print(self):
  324. self.display(pprint=True) # print results
  325. LOGGER.info(f'Speed: %.1fms pre-process, %.1fms inference, %.1fms NMS per image at shape {tuple(self.s)}' %
  326. self.t)
  327. def show(self):
  328. self.display(show=True) # show results
  329. def save(self, save_dir='runs/detect/exp'):
  330. save_dir = increment_path(save_dir, exist_ok=save_dir != 'runs/detect/exp', mkdir=True) # increment save_dir
  331. self.display(save=True, save_dir=save_dir) # save results
  332. def crop(self, save_dir='runs/detect/exp'):
  333. save_dir = increment_path(save_dir, exist_ok=save_dir != 'runs/detect/exp', mkdir=True) # increment save_dir
  334. self.display(crop=True, save_dir=save_dir) # crop results
  335. LOGGER.info(f'Saved results to {save_dir}\n')
  336. def render(self):
  337. self.display(render=True) # render results
  338. return self.imgs
  339. def pandas(self):
  340. # return detections as pandas DataFrames, i.e. print(results.pandas().xyxy[0])
  341. new = copy(self) # return copy
  342. ca = 'xmin', 'ymin', 'xmax', 'ymax', 'confidence', 'class', 'name' # xyxy columns
  343. cb = 'xcenter', 'ycenter', 'width', 'height', 'confidence', 'class', 'name' # xywh columns
  344. for k, c in zip(['xyxy', 'xyxyn', 'xywh', 'xywhn'], [ca, ca, cb, cb]):
  345. a = [[x[:5] + [int(x[5]), self.names[int(x[5])]] for x in x.tolist()] for x in getattr(self, k)] # update
  346. setattr(new, k, [pd.DataFrame(x, columns=c) for x in a])
  347. return new
  348. def tolist(self):
  349. # return a list of Detections objects, i.e. 'for result in results.tolist():'
  350. x = [Detections([self.imgs[i]], [self.pred[i]], self.names, self.s) for i in range(self.n)]
  351. for d in x:
  352. for k in ['imgs', 'pred', 'xyxy', 'xyxyn', 'xywh', 'xywhn']:
  353. setattr(d, k, getattr(d, k)[0]) # pop out of list
  354. return x
  355. def __len__(self):
  356. return self.n
  357. class Classify(nn.Module):
  358. # Classification head, i.e. x(b,c1,20,20) to x(b,c2)
  359. def __init__(self, c1, c2, k=1, s=1, p=None, g=1): # ch_in, ch_out, kernel, stride, padding, groups
  360. super().__init__()
  361. self.aap = nn.AdaptiveAvgPool2d(1) # to x(b,c1,1,1)
  362. self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g) # to x(b,c2,1,1)
  363. self.flat = nn.Flatten()
  364. def forward(self, x):
  365. z = torch.cat([self.aap(y) for y in (x if isinstance(x, list) else [x])], 1) # cat if list
  366. return self.flat(self.conv(z)) # flatten to x(b,c2)