├── README.md ├── images └── test.jpg ├── main.cpp └── main.py /README.md: -------------------------------------------------------------------------------- 1 | # hybridnets-opencv-dnn 2 | 使用OpenCV部署HybridNets,同时处理车辆检测、可驾驶区域分割、车道线分割,三项视觉感知任务,包含C++和Python两种版本的程序实现。本套程序只依赖opencv库就可以运行, 彻底摆脱对任何深度学习框架的依赖。 3 | 4 | 经测试验证,opencv4.6的可以加载onnx文件正常推理运行,opencv4.5和4.7都在推理运行时出现异常中断。 5 | 模型文件在百度云盘,下载链接是: 6 | 链接:https://pan.baidu.com/s/1FQ8yQ9G8L54xzr0jNoj5Hw 提取码:gpdb 7 | -------------------------------------------------------------------------------- /images/test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpc203/hybridnets-opencv-dnn/1f5dbc68f2b2fe942a9b6aeb19bbe200dffba394/images/test.jpg -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpc203/hybridnets-opencv-dnn/1f5dbc68f2b2fe942a9b6aeb19bbe200dffba394/main.cpp -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import argparse 3 | import numpy as np 4 | import os 5 | 6 | print(cv2.__version__) 7 | 8 | class HybridNets(): 9 | def __init__(self, modelpath, anchorpath, confThreshold=0.5, nmsThreshold=0.5): 10 | self.det_classes = ["car"] 11 | self.seg_classes = ["Background", "Lane", "Line"] 12 | 13 | self.net = cv2.dnn.readNet(modelpath) 14 | self.confThreshold = confThreshold 15 | self.nmsThreshold = nmsThreshold 16 | 17 | h, w = os.path.basename(modelpath).split('_')[-1].replace('.onnx', '').split('x') 18 | self.inpHeight, self.inpWidth = int(h), int(w) 19 | self.mean_ = np.array([0.485, 0.456, 0.406], dtype=np.float32).reshape((1, 1, 3)) 20 | self.std_ = np.array([0.229, 0.224, 0.225], dtype=np.float32).reshape((1, 1, 3)) 21 | self.anchors = np.load(anchorpath) ### cx_cy_w_h 22 | 23 | def resize_image(self, srcimg, keep_ratio=True): 24 | padh, padw, newh, neww = 0, 0, self.inpWidth, self.inpHeight 25 | if keep_ratio and srcimg.shape[0] != srcimg.shape[1]: 26 | hw_scale = srcimg.shape[0] / srcimg.shape[1] 27 | if hw_scale > 1: 28 | newh, neww = self.inpHeight, int(self.inpWidth / hw_scale) 29 | img = cv2.resize(srcimg, (neww, newh), interpolation=cv2.INTER_LINEAR) 30 | padw = int((self.inpWidth - neww) * 0.5) 31 | img = cv2.copyMakeBorder(img, 0, 0, padw, self.inpWidth - neww - padw, cv2.BORDER_CONSTANT, 32 | value=(114, 114, 114)) # add border 33 | else: 34 | newh, neww = int(self.inpHeight * hw_scale), self.inpWidth 35 | img = cv2.resize(srcimg, (neww, newh), interpolation=cv2.INTER_LINEAR) 36 | padh = int((self.inpHeight - newh) * 0.5) 37 | img = cv2.copyMakeBorder(img, padh, self.inpHeight - newh - padh, 0, 0, cv2.BORDER_CONSTANT, 38 | value=(114, 114, 114)) 39 | else: 40 | img = cv2.resize(srcimg, (self.inpWidth, self.inpHeight), interpolation=cv2.INTER_LINEAR) 41 | return img, newh, neww, padh, padw 42 | 43 | def detect(self, srcimg): 44 | img, newh, neww, padh, padw = self.resize_image(cv2.cvtColor(srcimg, cv2.COLOR_BGR2RGB)) 45 | scale_h, scale_w = srcimg.shape[0] / newh, srcimg.shape[1] / neww 46 | img = (img.astype(np.float32) / 255.0 - self.mean_) / self.std_ 47 | # Sets the input to the network 48 | blob = cv2.dnn.blobFromImage(img) 49 | self.net.setInput(blob) 50 | 51 | classification, box_regression, seg = self.net.forward(self.net.getUnconnectedOutLayersNames()) 52 | 53 | x_centers = box_regression[..., 1] * self.anchors[..., 2] + self.anchors[..., 0] 54 | y_centers = box_regression[..., 0] * self.anchors[..., 3] + self.anchors[..., 1] 55 | w = np.exp(box_regression[..., 3]) * self.anchors[..., 2] 56 | h = np.exp(box_regression[..., 2]) * self.anchors[..., 3] 57 | 58 | xmin = x_centers - w * 0.5 59 | ymin = y_centers - h * 0.5 60 | 61 | bboxes_wh = np.stack([xmin, ymin, w, h], axis=2).squeeze(axis=0) 62 | 63 | confidences = np.max(classification.squeeze(axis=0), axis=1) ####max_class_confidence 64 | classIds = np.argmax(classification.squeeze(axis=0), axis=1) 65 | mask = confidences > self.confThreshold 66 | bboxes_wh = bboxes_wh[mask] 67 | confidences = confidences[mask] 68 | classIds = classIds[mask] 69 | 70 | bboxes_wh -= np.array([[padw, padh, 0, 0]]) ### 还原回到原图, 合理使用广播法则 71 | bboxes_wh *= np.array([[scale_w, scale_h, scale_w, scale_h]]) 72 | 73 | indices = cv2.dnn.NMSBoxes(bboxes_wh.tolist(), confidences.tolist(), self.confThreshold, 74 | self.nmsThreshold).flatten().tolist() 75 | 76 | drive_area_mask = np.squeeze(seg, axis=0)[:, padh:(self.inpHeight - padh), padw:(self.inpWidth - padw)] 77 | seg_id = np.argmax(drive_area_mask, axis=0).astype(np.uint8) 78 | seg_id = cv2.resize(seg_id, (srcimg.shape[1], srcimg.shape[0]), interpolation=cv2.INTER_NEAREST) 79 | # drive_area_mask = cv2.resize(np.transpose(drive_area_mask, (1,2,0)), (srcimg.shape[1], srcimg.shape[0]), interpolation=cv2.INTER_NEAREST) 80 | # seg_id = np.argmax(drive_area_mask, axis=2).astype(np.uint8) 81 | 82 | outimg = srcimg.copy() 83 | for ind in indices: 84 | x, y, w, h = bboxes_wh[ind,:].astype(int) 85 | cv2.rectangle(outimg, (x, y), (x + w, y + h), (0, 0, 255), thickness=2, lineType=cv2.LINE_AA) 86 | cv2.putText(outimg, self.det_classes[classIds[ind]]+ ":" + str(round(confidences[ind], 2)), (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0, 0, 255), 87 | thickness=1, lineType=cv2.LINE_AA) 88 | 89 | outimg[seg_id == 1] = [0, 255, 0] 90 | outimg[seg_id == 2] = [255, 0, 0] 91 | return outimg 92 | 93 | 94 | if __name__ == "__main__": 95 | parser = argparse.ArgumentParser() 96 | parser.add_argument('--imgpath', type=str, default='images/test.jpg', help="image path") 97 | parser.add_argument('--modelpath', type=str, default='weights/hybridnets_256x384/hybridnets_256x384.onnx') 98 | parser.add_argument('--anchorpath', type=str, default='weights/hybridnets_256x384/anchors_256x384.npy') 99 | parser.add_argument('--confThreshold', default=0.3, type=float, help='class confidence') 100 | parser.add_argument('--nmsThreshold', default=0.5, type=float, help='nms iou thresh') 101 | args = parser.parse_args() 102 | 103 | yolonet = HybridNets(args.modelpath, args.anchorpath, confThreshold=args.confThreshold, 104 | nmsThreshold=args.nmsThreshold) 105 | srcimg = cv2.imread(args.imgpath) 106 | srcimg = yolonet.detect(srcimg) 107 | 108 | winName = 'Deep learning object detection use OpenCV' 109 | cv2.namedWindow(winName, 0) 110 | cv2.imshow(winName, srcimg) 111 | cv2.waitKey(0) 112 | cv2.destroyAllWindows() 113 | --------------------------------------------------------------------------------