基于Yolov7的路面病害检测代码
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.

experimental.py 11KB

10 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. import numpy as np
  2. import random
  3. import torch
  4. import torch.nn as nn
  5. from models.common import Conv, DWConv
  6. from utils.google_utils import attempt_download
  7. class CrossConv(nn.Module):
  8. # Cross Convolution Downsample
  9. def __init__(self, c1, c2, k=3, s=1, g=1, e=1.0, shortcut=False):
  10. # ch_in, ch_out, kernel, stride, groups, expansion, shortcut
  11. super(CrossConv, self).__init__()
  12. c_ = int(c2 * e) # hidden channels
  13. self.cv1 = Conv(c1, c_, (1, k), (1, s))
  14. self.cv2 = Conv(c_, c2, (k, 1), (s, 1), g=g)
  15. self.add = shortcut and c1 == c2
  16. def forward(self, x):
  17. return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))
  18. class Sum(nn.Module):
  19. # Weighted sum of 2 or more layers https://arxiv.org/abs/1911.09070
  20. def __init__(self, n, weight=False): # n: number of inputs
  21. super(Sum, self).__init__()
  22. self.weight = weight # apply weights boolean
  23. self.iter = range(n - 1) # iter object
  24. if weight:
  25. self.w = nn.Parameter(-torch.arange(1., n) / 2, requires_grad=True) # layer weights
  26. def forward(self, x):
  27. y = x[0] # no weight
  28. if self.weight:
  29. w = torch.sigmoid(self.w) * 2
  30. for i in self.iter:
  31. y = y + x[i + 1] * w[i]
  32. else:
  33. for i in self.iter:
  34. y = y + x[i + 1]
  35. return y
  36. class MixConv2d(nn.Module):
  37. # Mixed Depthwise Conv https://arxiv.org/abs/1907.09595
  38. def __init__(self, c1, c2, k=(1, 3), s=1, equal_ch=True):
  39. super(MixConv2d, self).__init__()
  40. groups = len(k)
  41. if equal_ch: # equal c_ per group
  42. i = torch.linspace(0, groups - 1E-6, c2).floor() # c2 indices
  43. c_ = [(i == g).sum() for g in range(groups)] # intermediate channels
  44. else: # equal weight.numel() per group
  45. b = [c2] + [0] * groups
  46. a = np.eye(groups + 1, groups, k=-1)
  47. a -= np.roll(a, 1, axis=1)
  48. a *= np.array(k) ** 2
  49. a[0] = 1
  50. c_ = np.linalg.lstsq(a, b, rcond=None)[0].round() # solve for equal weight indices, ax = b
  51. self.m = nn.ModuleList([nn.Conv2d(c1, int(c_[g]), k[g], s, k[g] // 2, bias=False) for g in range(groups)])
  52. self.bn = nn.BatchNorm2d(c2)
  53. self.act = nn.LeakyReLU(0.1, inplace=True)
  54. def forward(self, x):
  55. return x + self.act(self.bn(torch.cat([m(x) for m in self.m], 1)))
  56. class Ensemble(nn.ModuleList):
  57. # Ensemble of models
  58. def __init__(self):
  59. super(Ensemble, self).__init__()
  60. def forward(self, x, augment=False):
  61. y = []
  62. for module in self:
  63. y.append(module(x, augment)[0])
  64. # y = torch.stack(y).max(0)[0] # max ensemble
  65. # y = torch.stack(y).mean(0) # mean ensemble
  66. y = torch.cat(y, 1) # nms ensemble
  67. return y, None # inference, train output
  68. class ORT_NMS(torch.autograd.Function):
  69. '''ONNX-Runtime NMS operation'''
  70. @staticmethod
  71. def forward(ctx,
  72. boxes,
  73. scores,
  74. max_output_boxes_per_class=torch.tensor([100]),
  75. iou_threshold=torch.tensor([0.45]),
  76. score_threshold=torch.tensor([0.25])):
  77. device = boxes.device
  78. batch = scores.shape[0]
  79. num_det = random.randint(0, 100)
  80. batches = torch.randint(0, batch, (num_det,)).sort()[0].to(device)
  81. idxs = torch.arange(100, 100 + num_det).to(device)
  82. zeros = torch.zeros((num_det,), dtype=torch.int64).to(device)
  83. selected_indices = torch.cat([batches[None], zeros[None], idxs[None]], 0).T.contiguous()
  84. selected_indices = selected_indices.to(torch.int64)
  85. return selected_indices
  86. @staticmethod
  87. def symbolic(g, boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold):
  88. return g.op("NonMaxSuppression", boxes, scores, max_output_boxes_per_class, iou_threshold, score_threshold)
  89. class TRT_NMS(torch.autograd.Function):
  90. '''TensorRT NMS operation'''
  91. @staticmethod
  92. def forward(
  93. ctx,
  94. boxes,
  95. scores,
  96. background_class=-1,
  97. box_coding=1,
  98. iou_threshold=0.45,
  99. max_output_boxes=100,
  100. plugin_version="1",
  101. score_activation=0,
  102. score_threshold=0.25,
  103. ):
  104. batch_size, num_boxes, num_classes = scores.shape
  105. num_det = torch.randint(0, max_output_boxes, (batch_size, 1), dtype=torch.int32)
  106. det_boxes = torch.randn(batch_size, max_output_boxes, 4)
  107. det_scores = torch.randn(batch_size, max_output_boxes)
  108. det_classes = torch.randint(0, num_classes, (batch_size, max_output_boxes), dtype=torch.int32)
  109. return num_det, det_boxes, det_scores, det_classes
  110. @staticmethod
  111. def symbolic(g,
  112. boxes,
  113. scores,
  114. background_class=-1,
  115. box_coding=1,
  116. iou_threshold=0.45,
  117. max_output_boxes=100,
  118. plugin_version="1",
  119. score_activation=0,
  120. score_threshold=0.25):
  121. out = g.op("TRT::EfficientNMS_TRT",
  122. boxes,
  123. scores,
  124. background_class_i=background_class,
  125. box_coding_i=box_coding,
  126. iou_threshold_f=iou_threshold,
  127. max_output_boxes_i=max_output_boxes,
  128. plugin_version_s=plugin_version,
  129. score_activation_i=score_activation,
  130. score_threshold_f=score_threshold,
  131. outputs=4)
  132. nums, boxes, scores, classes = out
  133. return nums, boxes, scores, classes
  134. class ONNX_ORT(nn.Module):
  135. '''onnx module with ONNX-Runtime NMS operation.'''
  136. def __init__(self, max_obj=100, iou_thres=0.45, score_thres=0.25, max_wh=640, device=None, n_classes=80):
  137. super().__init__()
  138. self.device = device if device else torch.device("cpu")
  139. self.max_obj = torch.tensor([max_obj]).to(device)
  140. self.iou_threshold = torch.tensor([iou_thres]).to(device)
  141. self.score_threshold = torch.tensor([score_thres]).to(device)
  142. self.max_wh = max_wh # if max_wh != 0 : non-agnostic else : agnostic
  143. self.convert_matrix = torch.tensor([[1, 0, 1, 0], [0, 1, 0, 1], [-0.5, 0, 0.5, 0], [0, -0.5, 0, 0.5]],
  144. dtype=torch.float32,
  145. device=self.device)
  146. self.n_classes=n_classes
  147. def forward(self, x):
  148. boxes = x[:, :, :4]
  149. conf = x[:, :, 4:5]
  150. scores = x[:, :, 5:]
  151. if self.n_classes == 1:
  152. scores = conf # for models with one class, cls_loss is 0 and cls_conf is always 0.5,
  153. # so there is no need to multiplicate.
  154. else:
  155. scores *= conf # conf = obj_conf * cls_conf
  156. boxes @= self.convert_matrix
  157. max_score, category_id = scores.max(2, keepdim=True)
  158. dis = category_id.float() * self.max_wh
  159. nmsbox = boxes + dis
  160. max_score_tp = max_score.transpose(1, 2).contiguous()
  161. selected_indices = ORT_NMS.apply(nmsbox, max_score_tp, self.max_obj, self.iou_threshold, self.score_threshold)
  162. X, Y = selected_indices[:, 0], selected_indices[:, 2]
  163. selected_boxes = boxes[X, Y, :]
  164. selected_categories = category_id[X, Y, :].float()
  165. selected_scores = max_score[X, Y, :]
  166. X = X.unsqueeze(1).float()
  167. return torch.cat([X, selected_boxes, selected_categories, selected_scores], 1)
  168. class ONNX_TRT(nn.Module):
  169. '''onnx module with TensorRT NMS operation.'''
  170. def __init__(self, max_obj=100, iou_thres=0.45, score_thres=0.25, max_wh=None ,device=None, n_classes=80):
  171. super().__init__()
  172. assert max_wh is None
  173. self.device = device if device else torch.device('cpu')
  174. self.background_class = -1,
  175. self.box_coding = 1,
  176. self.iou_threshold = iou_thres
  177. self.max_obj = max_obj
  178. self.plugin_version = '1'
  179. self.score_activation = 0
  180. self.score_threshold = score_thres
  181. self.n_classes=n_classes
  182. def forward(self, x):
  183. boxes = x[:, :, :4]
  184. conf = x[:, :, 4:5]
  185. scores = x[:, :, 5:]
  186. if self.n_classes == 1:
  187. scores = conf # for models with one class, cls_loss is 0 and cls_conf is always 0.5,
  188. # so there is no need to multiplicate.
  189. else:
  190. scores *= conf # conf = obj_conf * cls_conf
  191. num_det, det_boxes, det_scores, det_classes = TRT_NMS.apply(boxes, scores, self.background_class, self.box_coding,
  192. self.iou_threshold, self.max_obj,
  193. self.plugin_version, self.score_activation,
  194. self.score_threshold)
  195. return num_det, det_boxes, det_scores, det_classes
  196. class End2End(nn.Module):
  197. '''export onnx or tensorrt model with NMS operation.'''
  198. def __init__(self, model, max_obj=100, iou_thres=0.45, score_thres=0.25, max_wh=None, device=None, n_classes=80):
  199. super().__init__()
  200. device = device if device else torch.device('cpu')
  201. assert isinstance(max_wh,(int)) or max_wh is None
  202. self.model = model.to(device)
  203. self.model.model[-1].end2end = True
  204. self.patch_model = ONNX_TRT if max_wh is None else ONNX_ORT
  205. self.end2end = self.patch_model(max_obj, iou_thres, score_thres, max_wh, device, n_classes)
  206. self.end2end.eval()
  207. def forward(self, x):
  208. x = self.model(x)
  209. x = self.end2end(x)
  210. return x
  211. def attempt_load(weights, map_location=None):
  212. # Loads an ensemble of models weights=[a,b,c] or a single model weights=[a] or weights=a
  213. model = Ensemble()
  214. for w in weights if isinstance(weights, list) else [weights]:
  215. attempt_download(w)
  216. ckpt = torch.load(w, map_location=map_location) # load
  217. model.append(ckpt['ema' if ckpt.get('ema') else 'model'].float().fuse().eval()) # FP32 model
  218. # Compatibility updates
  219. for m in model.modules():
  220. if type(m) in [nn.Hardswish, nn.LeakyReLU, nn.ReLU, nn.ReLU6, nn.SiLU]:
  221. m.inplace = True # pytorch 1.7.0 compatibility
  222. elif type(m) is nn.Upsample:
  223. m.recompute_scale_factor = None # torch 1.11.0 compatibility
  224. elif type(m) is Conv:
  225. m._non_persistent_buffers_set = set() # pytorch 1.6.0 compatibility
  226. if len(model) == 1:
  227. return model[-1] # return model
  228. else:
  229. print('Ensemble created with %s\n' % weights)
  230. for k in ['names', 'stride']:
  231. setattr(model, k, getattr(model[-1], k))
  232. return model # return ensemble