├── GUI.py ├── Lane_line ├── __pycache__ │ ├── ding.cpython-38.pyc │ └── lane.cpython-38.pyc └── lane.py ├── README.md ├── Yolov7 ├── __pycache__ │ └── yolo.cpython-38.pyc ├── model_data │ ├── simhei.ttf │ ├── voc_classes.txt │ └── yolo_anchors.txt ├── nets │ ├── __pycache__ │ │ ├── backbone.cpython-38.pyc │ │ └── yolo.cpython-38.pyc │ ├── backbone.py │ └── yolo.py ├── utils │ ├── __pycache__ │ │ ├── utils.cpython-38.pyc │ │ └── utils_bbox.cpython-38.pyc │ ├── utils.py │ └── utils_bbox.py └── yolo.py ├── deeplabv3 ├── __pycache__ │ └── deeplab.cpython-38.pyc ├── deeplab.py ├── nets │ ├── __pycache__ │ │ ├── deeplabv3_plus.cpython-38.pyc │ │ ├── mobilenetv2.cpython-38.pyc │ │ └── xception.cpython-38.pyc │ ├── deeplabv3_plus.py │ ├── mobilenetv2.py │ └── xception.py └── utils │ ├── __pycache__ │ └── utils.cpython-38.pyc │ └── utils.py ├── image ├── Road.jpg └── night_img1130.jpg └── main.py /GUI.py: -------------------------------------------------------------------------------- 1 | import time 2 | import tkinter as tk 3 | from tkinter import filedialog 4 | from PIL import Image, ImageTk 5 | from deeplabv3.deeplab import DeeplabV3 6 | from Yolov7.yolo import YOLO 7 | from PIL import Image 8 | import numpy as np 9 | import cv2 10 | from Lane_line.lane import Lane_line 11 | 12 | # 判断空变量 13 | Type_cap = None 14 | Type_name = None 15 | 16 | def Lane_draw(lane, img, image_thresh_binary): 17 | # 透视变换局部变量 18 | src_corners = [(415, 335), (585, 335), (1000, 600), (0, 600)] 19 | # dst_corners = [(0, 0), (600, 0), (600, 1000), (0, 1000)] 20 | dst_corners = [(0, 0), (1000, 0), (1000, 600), (0, 600)] 21 | M = cv2.getPerspectiveTransform(np.float32(src_corners), np.float32(dst_corners)) 22 | N = cv2.getPerspectiveTransform(np.float32(dst_corners), np.float32(src_corners)) 23 | # 透视变换成俯视图 24 | image_thresh_binary = lane.perspective_transform(image_thresh_binary, M) 25 | # 获取左右车道线的曲线坐标 26 | left_fit, right_fit, out_img, TF = lane.find_line_fit(np.array(image_thresh_binary)) 27 | if TF == 1: 28 | left_fitx, right_fitx, ploty = lane.get_fit_xy(out_img, left_fit, right_fit) 29 | # 绘制车道范围再透视变换回原图 30 | result = lane.project_back(np.array(image_thresh_binary), left_fitx, right_fitx, ploty) 31 | result = Lane(lane, result, np.array(img), left_fitx, right_fitx, N) 32 | return result 33 | else: 34 | return img 35 | 36 | def Lane(lane, result, img, pts_left, pts_right,N): 37 | left = np.int_(pts_left[549]) 38 | right = np.int_(pts_right[549]) 39 | left_right = [left, right, int((left+right)/2)] 40 | if len(left_right) > 1: 41 | image = cv2.line(result, (left_right[0], 549), (left_right[1], 549), (255, 0, 0), 3) 42 | image_blue_left = cv2.line(image, (left_right[0], 524), (left_right[0], 574), (255, 0, 0), 5) 43 | image_blue_right = cv2.line(image_blue_left, (left_right[1], 524), (left_right[1], 574), (255, 0, 0), 5) 44 | image_bule_center = cv2.line(image_blue_right, (left_right[2], 534), (left_right[2], 564), (255, 0, 0), 3) 45 | image_white_center = cv2.line(image_bule_center, (500, 524), (500, 574), (255, 255, 255), 5) 46 | 47 | if left_right[2] < 480: 48 | ation_text = "left" 49 | result = cv2.arrowedLine(np.array(image_white_center), (500, 549), (left_right[2], 549), (0, 0, 255), 50 | thickness=3, line_type=cv2.LINE_4, shift=0, tipLength=0.2) 51 | elif left_right[2] > 520: 52 | ation_text = "right" 53 | result = cv2.arrowedLine(np.array(image_white_center), (500, 549), (left_right[2], 549), (0, 0, 255), 54 | thickness=3, line_type=cv2.LINE_4, shift=0, tipLength=0.2) 55 | else: 56 | ation_text = "center" 57 | result = cv2.line(np.array(image_white_center), (480, 549), (520, 549), (255, 0, 255), 5) 58 | newwarp = lane.perspective_transform(result, N) 59 | result = cv2.addWeighted(img, 1, newwarp, 0.7, 0) 60 | result = cv2.putText(result, "ation: "+ation_text, (50, 50), cv2.FONT_HERSHEY_DUPLEX, 1,(0, 255, 0), 2) 61 | return result 62 | 63 | def keshihua(): 64 | global cap 65 | global capture 66 | global path 67 | global Type_name 68 | global Type_cap 69 | # 当开启摄像头时,识别输出 70 | if Type_cap == True: 71 | while capture.isOpened(): 72 | ret, frame = capture.read() 73 | if ret == True: 74 | img = cv2.resize(frame, (1000, 600)) 75 | img = Image.fromarray(np.uint8(img)) 76 | r_image_yolo = yolo.detect_image(img) 77 | r_image, image_thresh_binary = deeplab.detect_image(img) 78 | result = Lane_draw(lane, np.array(r_image_yolo), np.array(image_thresh_binary)) 79 | Show_Video(result, 700, 500) 80 | else: 81 | break 82 | 83 | # 当为照片时,进行识别输出 84 | if Type_name == True: 85 | img = cv2.imread(path) 86 | img = cv2.resize(img, (1000, 600)) 87 | img = Image.fromarray(np.uint8(img)) 88 | r_image_yolo = yolo.detect_image(img) 89 | r_image, image_thresh_binary = deeplab.detect_image(img) 90 | result = Lane_draw(lane, np.array(r_image_yolo), np.array(image_thresh_binary)) 91 | Show_Video(result, 700, 500) 92 | time.sleep(5) 93 | 94 | 95 | # 当为视频时,识别输出 96 | elif Type_name == False: 97 | # create_file() 98 | while cap.isOpened(): 99 | ret, frame = cap.read() 100 | if ret == True: 101 | img = cv2.resize(frame, (1000, 600)) 102 | img = Image.fromarray(np.uint8(img)) 103 | r_image_yolo = yolo.detect_image(img) 104 | r_image, image_thresh_binary = deeplab.detect_image(img) 105 | result = Lane_draw(lane, np.array(r_image_yolo), np.array(image_thresh_binary)) 106 | Show_Video(result, 700, 500) 107 | else: 108 | break 109 | 110 | 111 | 112 | 113 | def Yolov7(): 114 | # 当开启摄像头时,识别输出 115 | if Type_cap == True: 116 | while capture.isOpened(): 117 | ret, frame = capture.read() 118 | if ret == True: 119 | img = cv2.resize(frame, (1000, 600)) 120 | img = Image.fromarray(np.uint8(img)) 121 | r_image_yolo = yolo.detect_image(img) 122 | Show_Video(r_image_yolo, 700, 500) 123 | else: 124 | break 125 | 126 | # 当为照片时,进行识别输出 127 | if Type_name == True: 128 | img = cv2.imread(path) 129 | img = cv2.resize(img, (1000, 600)) 130 | img = Image.fromarray(np.uint8(img)) 131 | r_image_yolo = yolo.detect_image(img) 132 | Show_Video(r_image_yolo, 700, 500) 133 | time.sleep(5) 134 | 135 | # 当为视频时,识别输出 136 | elif Type_name == False: 137 | # create_file() 138 | while cap.isOpened(): 139 | ret, frame = cap.read() 140 | if ret == True: 141 | img = cv2.resize(frame, (1000, 600)) 142 | img = Image.fromarray(np.uint8(img)) 143 | r_image_yolo = yolo.detect_image(img) 144 | Show_Video(r_image_yolo, 700, 500) 145 | else: 146 | break 147 | 148 | def Deeplabv3(): 149 | # 当开启摄像头时,识别输出 150 | if Type_cap == True: 151 | while capture.isOpened(): 152 | ret, frame = capture.read() 153 | if ret == True: 154 | img = cv2.resize(frame, (1000, 600)) 155 | img = Image.fromarray(np.uint8(img)) 156 | r_image, image_thresh_binary = deeplab.detect_image(img) 157 | Show_Video(r_image, 700, 500) 158 | else: 159 | break 160 | 161 | 162 | # 当为照片时,进行识别输出 163 | if Type_name == True: 164 | img = cv2.imread(path) 165 | img = cv2.resize(img, (1000, 600)) 166 | img = Image.fromarray(np.uint8(img)) 167 | r_image, image_thresh_binary = deeplab.detect_image(img) 168 | Show_Video(r_image, 700, 500) 169 | time.sleep(5) 170 | 171 | 172 | # 当为视频时,识别输出 173 | elif Type_name == False: 174 | while cap.isOpened(): 175 | ret, frame = cap.read() 176 | if ret == True: 177 | img = cv2.resize(frame, (1000, 600)) 178 | img = Image.fromarray(np.uint8(img)) 179 | r_image, image_thresh_binary = deeplab.detect_image(img) 180 | Show_Video(r_image, 700, 500) 181 | else: 182 | break 183 | 184 | 185 | 186 | # 桌面清除功能 187 | def Desktop(): 188 | Video_canvas.delete('path') 189 | Video_canvas.delete('video') 190 | # 191 | # 设置背景图片缩放功能 192 | def get_image(filename, width, height): 193 | im = Image.open(filename).resize((width, height)) 194 | return ImageTk.PhotoImage(im) 195 | 196 | # 读取视频转换为帧动画 197 | def Read_Video(cap, w_, h_): 198 | ref, frame = cap.read() 199 | tkimage = Resize_Video(frame, w_, h_) 200 | return tkimage 201 | 202 | # 将帧动画按一定比例缩小 203 | def Resize_Video(frame, w_, h_): 204 | cvimage = cv2.cvtColor(np.array(frame), cv2.COLOR_BGR2RGB) 205 | pilImage = Image.fromarray(cvimage) 206 | pilImage = pilImage.resize((w_, h_), Image.ANTIALIAS) 207 | tkimage = ImageTk.PhotoImage(image=pilImage) 208 | return tkimage 209 | 210 | # 将帧动画显示到窗口 211 | def Show_Video(frame, w_, h_): 212 | tkmask = Resize_Video(frame, w_, h_) 213 | Video_canvas.create_image(0, 0, anchor='nw', image=tkmask) 214 | Video_canvas.update() 215 | Video_canvas.after(1) 216 | 217 | 218 | # 标签清除功能 219 | def Label_clear(): 220 | strPathVar.set("") 221 | 222 | # 选择文件的途径并显示出来 223 | def path_select(): 224 | global cap 225 | global srcImage 226 | global path 227 | global Type_name 228 | global Type_cap 229 | Label_clear() 230 | path = filedialog.askopenfilename() 231 | photo = ['.jpg', '.png', '.bmp', 'jpeg'] 232 | video = ['.mp4', '.mov'] 233 | for i in photo: 234 | if i in path.lower(): 235 | frame = cv2.imread(path) 236 | srcImage = Resize_Video(frame, 700, 500) 237 | Video_canvas.create_image(350, 250, anchor='center', image=srcImage, tag="path") 238 | Video_canvas.update() 239 | Video_canvas.after(1) 240 | strPathVar.set(path) 241 | Type_name = True 242 | break 243 | else: 244 | for j in video: 245 | if j in path.lower(): 246 | cap = cv2.VideoCapture(path) 247 | strPathVar.set(path) 248 | Type_name = False 249 | while True: 250 | picture = Read_Video(cap, 700, 500) 251 | Video_canvas.create_image(0, 0, anchor='nw', image=picture, tag="video") 252 | Video_canvas.update() 253 | Video_canvas.after(1) 254 | continue 255 | break 256 | else: 257 | string = "图片(jpg,png,bmp,jpeg), 视频(mp4,mov)" 258 | strPathVar.set(string) 259 | break 260 | 261 | # 选择文件的途径并显示出来 262 | def Model_path_select(): 263 | global Model_path 264 | Model_path = filedialog.askopenfilename() 265 | data_pth = ".pth" 266 | if data_pth in Model_path.lower(): 267 | strModel_data.set(Model_path) 268 | deeplab._defaults["model_path"] = strModel_data 269 | else: 270 | string = "请输入正确语义分割权重(.pth)" 271 | strModel_data.set(string) 272 | 273 | 274 | # 打开摄像头 275 | def Open_camera( ): 276 | global capture 277 | global Type_cap 278 | Label_clear() 279 | Type_cap = True 280 | Camera(Type_cap) 281 | 282 | # 关闭摄像头 283 | def Close_camera(): 284 | global Type_cpa 285 | Type_cap = False 286 | Camera(Type_cap) 287 | Desktop() 288 | Label_clear() 289 | 290 | # 摄像头的显示功能 291 | def Camera(Type_cap): 292 | global capture 293 | capture = cv2.VideoCapture(0) 294 | while capture.isOpened(): 295 | if Type_cap == False: 296 | capture.release() 297 | break 298 | ref, frame = capture.read() 299 | picture = Resize_Video(frame, 700, 500) 300 | Video_canvas.create_image(0, 0, anchor='nw', image=picture) 301 | Video_canvas.update() 302 | Video_canvas.after(1) 303 | if Type_cap == False: 304 | capture.release() 305 | break 306 | if __name__ == '__main__': 307 | yolo = YOLO() 308 | deeplab = DeeplabV3() 309 | lane = Lane_line() 310 | 311 | 312 | # 设置窗口基本信息 313 | win = tk.Tk() 314 | win.title("车道线检测系统") 315 | win.geometry("1000x600") 316 | win.resizable(height=False, width=False) 317 | 318 | strPathVar = tk.StringVar() 319 | strModel_data = tk.StringVar() 320 | 321 | 322 | # 标题区域 323 | title_box = tk.Frame(win, width=1000, height=100, borderwidth=0, bg="white") 324 | title_box.place(x=0) 325 | # 添加标题 326 | title = tk.Label(title_box, text="车道线检测系统", fg="black", font=("宋体", 30), bg="white") 327 | title.place(x=350, y=25) 328 | 329 | 330 | # 显示区域 331 | show_box = tk.Frame(win, width=700, height=500, borderwidth=0, bg="gray", relief="groove") 332 | show_box.place(x=0, y=100) 333 | # 视频显示 334 | Video_canvas = tk.Canvas(show_box, width=700, height=500, borderwidth=2, bg='black') 335 | im2 = get_image("./image/Road.jpg", 700, 500) 336 | Video_canvas.create_image(350, 250, image=im2) 337 | Video_canvas.pack() 338 | 339 | 340 | # 功能区域 341 | work_box = tk.Frame(win, width=300, height=500, borderwidth=0, bg="yellow") 342 | work_box.place(x=700, y=102) 343 | 344 | #打开本地图像或视频 345 | open_box = tk.Frame(work_box, width=300, height=130, borderwidth=0, bg="lightblue") 346 | open_box.place(x=0, y=0) 347 | # 链接显示 348 | choose_show = tk.Entry(open_box, width=35, textvariable=strPathVar) 349 | choose_show.place(x=25, y=40) 350 | # 添加按键 351 | choose = tk.Button(open_box, width=35, text="选择文件", bg="lightgray", command=path_select) 352 | choose.place(x=25, y=90) 353 | 354 | # 权重选择 355 | weight_box = tk.Frame(work_box, width=300, height=130, borderwidth=0, bg="lightblue") 356 | weight_box.place(x=0, y=130) 357 | # 链接显示 358 | weight_show = tk.Entry(weight_box, width=35, textvariable=strModel_data) 359 | weight_show.place(x=25, y=30) 360 | # 按键 361 | weight_button = tk.Button(weight_box, width=35, text="选择权重", bg="lightgray", command=Model_path_select) 362 | weight_button.place(x=25, y=80) 363 | 364 | #摄像头操控区域 365 | camera_box = tk.Frame(work_box, width= 300, height=50, borderwidth=0, bg="lightblue") 366 | camera_box.place(x=0, y=260) 367 | # 打开摄像头 368 | open_camera = tk.Button(camera_box, text="打开摄像头", width=10, bg="lightgray", command=Open_camera) 369 | open_camera.place(x=30, y=12) 370 | # 关闭摄像头 371 | close_camera = tk.Button(camera_box, text="关闭摄像头", width=10, bg="lightgray", command=Close_camera) 372 | close_camera.place(x=190, y=12) 373 | 374 | # 按键区域 375 | button_box = tk.Frame(work_box, width=300, height=370, borderwidth=0, bg="lightblue") 376 | button_box.place(x=0, y=310) 377 | 378 | # 目标检测 379 | detection = tk.Button(button_box, width=35, text="目标检测", bg="lightgray", command=Yolov7) 380 | detection.place(x=25, y=25) 381 | # 语义分割 382 | split = tk.Button(button_box, width=35, text="语义分割", bg="lightgray", command=Deeplabv3) 383 | split.place(x=25, y=75) 384 | #检测可视化 385 | check = tk.Button(button_box, width=35, text="检测可视化", bg="lightgray", command=keshihua) 386 | check.place(x=25, y=115) 387 | 388 | win.mainloop() 389 | 390 | -------------------------------------------------------------------------------- /Lane_line/__pycache__/ding.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joecoss/Lane-detection/4ebe7db47bea6eb07548ca5c8b4678fcfdc1a513/Lane_line/__pycache__/ding.cpython-38.pyc -------------------------------------------------------------------------------- /Lane_line/__pycache__/lane.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joecoss/Lane-detection/4ebe7db47bea6eb07548ca5c8b4678fcfdc1a513/Lane_line/__pycache__/lane.cpython-38.pyc -------------------------------------------------------------------------------- /Lane_line/lane.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | 4 | class Lane_line(object): 5 | # 透视变换 6 | def perspective_transform(self, img, M_N): 7 | img_size = (img.shape[1], img.shape[0]) 8 | warped = cv2.warpPerspective(img, M_N, img_size, flags=cv2.INTER_LINEAR) 9 | return warped 10 | 11 | # 左右车道线的曲线拟合 12 | def find_line_fit(self, img): 13 | # 滑动窗口的数量 14 | nwindows = 50 15 | # 设置x的检测范围,滑动窗口的宽度的一半,手动指定 16 | margin = 100 17 | # 设置最小像素点,阈值用于统计滑动窗口区域内的非零像素个数,小于50的窗口不对x的中心值进行更新 18 | minpix = 50 19 | # 1.确定左右车道线的位置 20 | # 统计直方图 21 | histogram = np.sum(img[img.shape[0] // 2:, :], axis=0) 22 | out_img = np.dstack((img, img, img)) * 255 23 | # 在统计结果中找到左右最大的点的位置,作为左右车道检测的开始点 24 | # 将统计结果一分为二,划分为左右两个部分,分别定位峰值位置,即为两条车道的搜索位置 25 | midpoint = np.int(histogram.shape[0] / 2) 26 | leftx_base = np.argmax(histogram[:midpoint]) 27 | rightx_base = np.argmax(histogram[midpoint:]) + midpoint 28 | 29 | 30 | window_height = np.int(img.shape[0] / nwindows) 31 | # 获取图像中不为0的点 32 | nonzero = img.nonzero() 33 | nonzeroy = np.array(nonzero[0]) 34 | nonzerox = np.array(nonzero[1]) 35 | # 车道检测的当前位置 36 | leftx_current = leftx_base 37 | rightx_current = rightx_base 38 | # 用来记录搜索窗口中非零点在nonzeroy和nonzerox中的索引 39 | left_lane_inds = [] 40 | right_lane_inds = [] 41 | 42 | 43 | for window in range(nwindows): 44 | # 设置窗口的y的检测范围,因为图像是(行列),shape[0]表示y方向的结果,上面是0 45 | win_y_low = img.shape[0] - (window + 1) * window_height 46 | win_y_high = img.shape[0] - window * window_height 47 | # 左车道x的范围 48 | win_xleft_low = leftx_current - margin 49 | win_xleft_high = leftx_current + margin 50 | # 右车道x的范围 51 | win_xright_low = rightx_current - margin 52 | win_xright_high = rightx_current + margin 53 | 54 | cv2.rectangle(out_img, (win_xleft_low, win_y_low), (win_xleft_high, win_y_high), 55 | (0, 255, 0), 2) 56 | cv2.rectangle(out_img, (win_xright_low, win_y_low), (win_xright_high, win_y_high), 57 | (0, 255, 0), 2) 58 | # 确定非零点的位置x,y是否在搜索窗口中,将在搜索窗口内的x,y的索引存入left_lane_inds和right_lane_inds中 59 | good_left_inds = ((nonzeroy >= win_y_low) & (nonzeroy < win_y_high) & 60 | (nonzerox >= win_xleft_low) & (nonzerox < win_xleft_high)).nonzero()[0] 61 | good_right_inds = ((nonzeroy >= win_y_low) & (nonzeroy < win_y_high) & 62 | (nonzerox >= win_xright_low) & (nonzerox < win_xright_high)).nonzero()[0] 63 | left_lane_inds.append(good_left_inds) 64 | right_lane_inds.append(good_right_inds) 65 | # 如果获取的点的个数大于最小个数,则利用其更新滑动窗口在x轴的位置 66 | if len(good_left_inds) > minpix: 67 | leftx_current = np.int(np.mean(nonzerox[good_left_inds])) 68 | if len(good_right_inds) > minpix: 69 | rightx_current = np.int(np.mean(nonzerox[good_right_inds])) 70 | # 将检测出的左右车道点转换为array 71 | left_lane_inds = np.concatenate(left_lane_inds) 72 | right_lane_inds = np.concatenate(right_lane_inds) 73 | # 获取检测出的左右车道点在图像中的位置 74 | leftx = nonzerox[left_lane_inds] 75 | lefty = nonzeroy[left_lane_inds] 76 | rightx = nonzerox[right_lane_inds] 77 | righty = nonzeroy[right_lane_inds] 78 | out_img[nonzeroy[left_lane_inds], nonzerox[left_lane_inds]] = [255, 0, 0] 79 | out_img[nonzeroy[right_lane_inds], nonzerox[right_lane_inds]] = [0, 0, 255] 80 | try: 81 | # 3.用曲线拟合检测出的点,二次多项式拟合,返回的结果是系数 82 | left_fit = np.polyfit(lefty, leftx, 2) 83 | right_fit = np.polyfit(righty, rightx, 2) 84 | return left_fit, right_fit, out_img, 1 85 | except: 86 | return 0, 0, 0, 0 87 | 88 | def get_fit_xy(self, img, left_fit, right_fit): 89 | ploty = np.linspace(0, img.shape[0] - 1, img.shape[0]) 90 | left_fitx = left_fit[0] * ploty ** 2 + left_fit[1] * ploty + left_fit[2] 91 | right_fitx = right_fit[0] * ploty ** 2 + right_fit[1] * ploty + right_fit[2] 92 | return left_fitx, right_fitx, ploty 93 | 94 | # 将获取的拟合曲线绘制车道区域掩膜 95 | def project_back(self, wrap_img, left_fitx, right_fitx, ploty): 96 | warp_zero = np.zeros_like(wrap_img).astype(np.uint8) 97 | color_warp = np.dstack((warp_zero, warp_zero, warp_zero)) 98 | pts_left = np.array([np.transpose(np.vstack([left_fitx, ploty]))]) 99 | pts_right = np.array([np.flipud(np.transpose(np.vstack([right_fitx, ploty])))]) 100 | pts = np.hstack((pts_left, pts_right)) 101 | cv2.fillPoly(color_warp, np.int_([pts]), (0, 255, 0)) 102 | return color_warp -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lane detection 2 | 本项目针对传统车道线检测鲁棒性较差的现象,通过运用YOLOV7与DeepLabv3+的图像深度学习算法对特定数据集进行模型训练,开发了一款能调用车载摄像头来识别道路环境,并用语音告知驾驶员偏离车道,前方车距等驾驶信息的偏离预警系统,辅助驾驶员更加安全高效的行驶。 3 | ![可视化](https://user-images.githubusercontent.com/89328970/220098900-36f5d74c-816d-4b66-95ee-b54f9d7d1861.JPG) 4 | 5 | # GUI 6 | ![image](https://user-images.githubusercontent.com/89328970/220099687-7fb6ae20-8f9e-4df9-b516-f2961db1353c.png) 7 | # 参考文献 8 | [1]Chien-Yao Wang、Alexey Bochkovskiy、Hong-Yuan Mark Liao, 《YOLOv7: Trainable bag-of-freebies sets new state-of-the-art for real-time object detectors》, https://www.semanticscholar.org/reader/3aed4648f7857c1d5e9b1da4c3afaf97463138c3 , 访问时间 9 | [2]Liang-Chieh Chen、 Yukun Zhu、 George Papandreou etc, 《Encoder-Decoder with Atrous Separable Convolution for Semantic Image Segmentation》, 1802.02611.pdf (arxiv.org), 访问时间 10 | [3]CSDN(2022),(67条消息) 睿智的目标检测61——Pytorch搭建YoloV7目标检测平台_Bubbliiiing的博客-CSDN博客_pytorch yolov7 11 | [4]Github(2022),YOLOV:You Only Look Once目标检测模型在pytorch当中的实现,GitHub-bubbliiiing/yolov7-pytorch,访问时间 12 | [5]Github(2022),DeepLabv3+:Encoder-Decoder with Atrous Separable Convolution语义分割模型在pytorch当中的实现,GitHub - bubbliiiing/deeplabv3-plus-pytorch 13 | [6]Github(2022),高级寻车项目,GitHub - ajsmilutin/CarND-Advanced-Lane-Lines 14 | [7]Github(2022),飞桨ELSeg,PaddleSeg/EISeg at release/2.6·PaddlePaddle/PaddleSeg·GitHub 15 | [8]python软件基金会, tkinter-python接口到Tcl/Tk, tkinter — Python 接口到 Tcl/Tk — Python 3.11.1 文档 16 | [9]阿斯顿·张、李沐、亚历山大·J·斯莫拉, 动手学深度学习(M), 人民邮电出版 17 | -------------------------------------------------------------------------------- /Yolov7/__pycache__/yolo.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joecoss/Lane-detection/4ebe7db47bea6eb07548ca5c8b4678fcfdc1a513/Yolov7/__pycache__/yolo.cpython-38.pyc -------------------------------------------------------------------------------- /Yolov7/model_data/simhei.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joecoss/Lane-detection/4ebe7db47bea6eb07548ca5c8b4678fcfdc1a513/Yolov7/model_data/simhei.ttf -------------------------------------------------------------------------------- /Yolov7/model_data/voc_classes.txt: -------------------------------------------------------------------------------- 1 | person 2 | bicycle 3 | car 4 | motorbike 5 | bus -------------------------------------------------------------------------------- /Yolov7/model_data/yolo_anchors.txt: -------------------------------------------------------------------------------- 1 | 12, 16, 19, 36, 40, 28, 36, 75, 76, 55, 72, 146, 142, 110, 192, 243, 459, 401 -------------------------------------------------------------------------------- /Yolov7/nets/__pycache__/backbone.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joecoss/Lane-detection/4ebe7db47bea6eb07548ca5c8b4678fcfdc1a513/Yolov7/nets/__pycache__/backbone.cpython-38.pyc -------------------------------------------------------------------------------- /Yolov7/nets/__pycache__/yolo.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joecoss/Lane-detection/4ebe7db47bea6eb07548ca5c8b4678fcfdc1a513/Yolov7/nets/__pycache__/yolo.cpython-38.pyc -------------------------------------------------------------------------------- /Yolov7/nets/backbone.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | 4 | 5 | def autopad(k, p=None): 6 | if p is None: 7 | p = k // 2 if isinstance(k, int) else [x // 2 for x in k] 8 | return p 9 | 10 | class SiLU(nn.Module): 11 | @staticmethod 12 | def forward(x): 13 | return x * torch.sigmoid(x) 14 | 15 | class Conv(nn.Module): 16 | def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=SiLU()): # ch_in, ch_out, kernel, stride, padding, groups 17 | super(Conv, self).__init__() 18 | self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False) 19 | self.bn = nn.BatchNorm2d(c2, eps=0.001, momentum=0.03) 20 | self.act = nn.LeakyReLU(0.1, inplace=True) if act is True else (act if isinstance(act, nn.Module) else nn.Identity()) 21 | 22 | def forward(self, x): 23 | return self.act(self.bn(self.conv(x))) 24 | 25 | def fuseforward(self, x): 26 | return self.act(self.conv(x)) 27 | 28 | class Multi_Concat_Block(nn.Module): 29 | def __init__(self, c1, c2, c3, n=4, e=1, ids=[0]): 30 | super(Multi_Concat_Block, self).__init__() 31 | c_ = int(c2 * e) 32 | 33 | self.ids = ids 34 | self.cv1 = Conv(c1, c_, 1, 1) 35 | self.cv2 = Conv(c1, c_, 1, 1) 36 | self.cv3 = nn.ModuleList( 37 | [Conv(c_ if i ==0 else c2, c2, 3, 1) for i in range(n)] 38 | ) 39 | self.cv4 = Conv(c_ * 2 + c2 * (len(ids) - 2), c3, 1, 1) 40 | 41 | def forward(self, x): 42 | x_1 = self.cv1(x) 43 | x_2 = self.cv2(x) 44 | 45 | x_all = [x_1, x_2] 46 | for i in range(len(self.cv3)): 47 | x_2 = self.cv3[i](x_2) 48 | x_all.append(x_2) 49 | 50 | out = self.cv4(torch.cat([x_all[id] for id in self.ids], 1)) 51 | return out 52 | 53 | class MP(nn.Module): 54 | def __init__(self, k=2): 55 | super(MP, self).__init__() 56 | self.m = nn.MaxPool2d(kernel_size=k, stride=k) 57 | 58 | def forward(self, x): 59 | return self.m(x) 60 | 61 | class Transition_Block(nn.Module): 62 | def __init__(self, c1, c2): 63 | super(Transition_Block, self).__init__() 64 | self.cv1 = Conv(c1, c2, 1, 1) 65 | self.cv2 = Conv(c1, c2, 1, 1) 66 | self.cv3 = Conv(c2, c2, 3, 2) 67 | 68 | self.mp = MP() 69 | 70 | def forward(self, x): 71 | x_1 = self.mp(x) 72 | x_1 = self.cv1(x_1) 73 | 74 | x_2 = self.cv2(x) 75 | x_2 = self.cv3(x_2) 76 | 77 | return torch.cat([x_2, x_1], 1) 78 | 79 | class Backbone(nn.Module): 80 | def __init__(self, transition_channels, block_channels, n, phi, pretrained=False): 81 | super().__init__() 82 | #-----------------------------------------------# 83 | # 输入图片是640, 640, 3 84 | #-----------------------------------------------# 85 | ids = { 86 | 'l' : [-1, -3, -5, -6], 87 | 'x' : [-1, -3, -5, -7, -8], 88 | }[phi] 89 | self.stem = nn.Sequential( 90 | Conv(3, transition_channels, 3, 1), 91 | Conv(transition_channels, transition_channels * 2, 3, 2), 92 | Conv(transition_channels * 2, transition_channels * 2, 3, 1), 93 | ) 94 | self.dark2 = nn.Sequential( 95 | Conv(transition_channels * 2, transition_channels * 4, 3, 2), 96 | Multi_Concat_Block(transition_channels * 4, block_channels * 2, transition_channels * 8, n=n, ids=ids), 97 | ) 98 | self.dark3 = nn.Sequential( 99 | Transition_Block(transition_channels * 8, transition_channels * 4), 100 | Multi_Concat_Block(transition_channels * 8, block_channels * 4, transition_channels * 16, n=n, ids=ids), 101 | ) 102 | self.dark4 = nn.Sequential( 103 | Transition_Block(transition_channels * 16, transition_channels * 8), 104 | Multi_Concat_Block(transition_channels * 16, block_channels * 8, transition_channels * 32, n=n, ids=ids), 105 | ) 106 | self.dark5 = nn.Sequential( 107 | Transition_Block(transition_channels * 32, transition_channels * 16), 108 | Multi_Concat_Block(transition_channels * 32, block_channels * 8, transition_channels * 32, n=n, ids=ids), 109 | ) 110 | 111 | if pretrained: 112 | url = { 113 | "l" : 'https://github.com/bubbliiiing/yolov7-pytorch/releases/download/v1.0/yolov7_backbone_weights.pth', 114 | "x" : 'https://github.com/bubbliiiing/yolov7-pytorch/releases/download/v1.0/yolov7_x_backbone_weights.pth', 115 | }[phi] 116 | checkpoint = torch.hub.load_state_dict_from_url(url=url, map_location="cpu", model_dir="./model_data") 117 | self.load_state_dict(checkpoint, strict=False) 118 | print("Load weights from " + url.split('/')[-1]) 119 | 120 | def forward(self, x): 121 | x = self.stem(x) 122 | x = self.dark2(x) 123 | #-----------------------------------------------# 124 | # dark3的输出为80, 80, 256,是一个有效特征层 125 | #-----------------------------------------------# 126 | x = self.dark3(x) 127 | feat1 = x 128 | #-----------------------------------------------# 129 | # dark4的输出为40, 40, 512,是一个有效特征层 130 | #-----------------------------------------------# 131 | x = self.dark4(x) 132 | feat2 = x 133 | #-----------------------------------------------# 134 | # dark5的输出为20, 20, 1024,是一个有效特征层 135 | #-----------------------------------------------# 136 | x = self.dark5(x) 137 | feat3 = x 138 | return feat1, feat2, feat3 139 | -------------------------------------------------------------------------------- /Yolov7/nets/yolo.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import torch 3 | import torch.nn as nn 4 | 5 | from Yolov7.nets.backbone import Backbone, Multi_Concat_Block, Conv, SiLU, Transition_Block, autopad 6 | 7 | 8 | class SPPCSPC(nn.Module): 9 | # CSP https://github.com/WongKinYiu/CrossStagePartialNetworks 10 | def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5, k=(5, 9, 13)): 11 | super(SPPCSPC, self).__init__() 12 | c_ = int(2 * c2 * e) # hidden channels 13 | self.cv1 = Conv(c1, c_, 1, 1) 14 | self.cv2 = Conv(c1, c_, 1, 1) 15 | self.cv3 = Conv(c_, c_, 3, 1) 16 | self.cv4 = Conv(c_, c_, 1, 1) 17 | self.m = nn.ModuleList([nn.MaxPool2d(kernel_size=x, stride=1, padding=x // 2) for x in k]) 18 | self.cv5 = Conv(4 * c_, c_, 1, 1) 19 | self.cv6 = Conv(c_, c_, 3, 1) 20 | self.cv7 = Conv(2 * c_, c2, 1, 1) 21 | 22 | def forward(self, x): 23 | x1 = self.cv4(self.cv3(self.cv1(x))) 24 | y1 = self.cv6(self.cv5(torch.cat([x1] + [m(x1) for m in self.m], 1))) 25 | y2 = self.cv2(x) 26 | return self.cv7(torch.cat((y1, y2), dim=1)) 27 | 28 | class RepConv(nn.Module): 29 | # Represented convolution 30 | # https://arxiv.org/abs/2101.03697 31 | def __init__(self, c1, c2, k=3, s=1, p=None, g=1, act=SiLU(), deploy=False): 32 | super(RepConv, self).__init__() 33 | self.deploy = deploy 34 | self.groups = g 35 | self.in_channels = c1 36 | self.out_channels = c2 37 | 38 | assert k == 3 39 | assert autopad(k, p) == 1 40 | 41 | padding_11 = autopad(k, p) - k // 2 42 | self.act = nn.LeakyReLU(0.1, inplace=True) if act is True else (act if isinstance(act, nn.Module) else nn.Identity()) 43 | 44 | if deploy: 45 | self.rbr_reparam = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=True) 46 | else: 47 | self.rbr_identity = (nn.BatchNorm2d(num_features=c1, eps=0.001, momentum=0.03) if c2 == c1 and s == 1 else None) 48 | self.rbr_dense = nn.Sequential( 49 | nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False), 50 | nn.BatchNorm2d(num_features=c2, eps=0.001, momentum=0.03), 51 | ) 52 | self.rbr_1x1 = nn.Sequential( 53 | nn.Conv2d( c1, c2, 1, s, padding_11, groups=g, bias=False), 54 | nn.BatchNorm2d(num_features=c2, eps=0.001, momentum=0.03), 55 | ) 56 | 57 | def forward(self, inputs): 58 | if hasattr(self, "rbr_reparam"): 59 | return self.act(self.rbr_reparam(inputs)) 60 | if self.rbr_identity is None: 61 | id_out = 0 62 | else: 63 | id_out = self.rbr_identity(inputs) 64 | return self.act(self.rbr_dense(inputs) + self.rbr_1x1(inputs) + id_out) 65 | 66 | def get_equivalent_kernel_bias(self): 67 | kernel3x3, bias3x3 = self._fuse_bn_tensor(self.rbr_dense) 68 | kernel1x1, bias1x1 = self._fuse_bn_tensor(self.rbr_1x1) 69 | kernelid, biasid = self._fuse_bn_tensor(self.rbr_identity) 70 | return ( 71 | kernel3x3 + self._pad_1x1_to_3x3_tensor(kernel1x1) + kernelid, 72 | bias3x3 + bias1x1 + biasid, 73 | ) 74 | 75 | def _pad_1x1_to_3x3_tensor(self, kernel1x1): 76 | if kernel1x1 is None: 77 | return 0 78 | else: 79 | return nn.functional.pad(kernel1x1, [1, 1, 1, 1]) 80 | 81 | def _fuse_bn_tensor(self, branch): 82 | if branch is None: 83 | return 0, 0 84 | if isinstance(branch, nn.Sequential): 85 | kernel = branch[0].weight 86 | running_mean = branch[1].running_mean 87 | running_var = branch[1].running_var 88 | gamma = branch[1].weight 89 | beta = branch[1].bias 90 | eps = branch[1].eps 91 | else: 92 | assert isinstance(branch, nn.BatchNorm2d) 93 | if not hasattr(self, "id_tensor"): 94 | input_dim = self.in_channels // self.groups 95 | kernel_value = np.zeros( 96 | (self.in_channels, input_dim, 3, 3), dtype=np.float32 97 | ) 98 | for i in range(self.in_channels): 99 | kernel_value[i, i % input_dim, 1, 1] = 1 100 | self.id_tensor = torch.from_numpy(kernel_value).to(branch.weight.device) 101 | kernel = self.id_tensor 102 | running_mean = branch.running_mean 103 | running_var = branch.running_var 104 | gamma = branch.weight 105 | beta = branch.bias 106 | eps = branch.eps 107 | std = (running_var + eps).sqrt() 108 | t = (gamma / std).reshape(-1, 1, 1, 1) 109 | return kernel * t, beta - running_mean * gamma / std 110 | 111 | def repvgg_convert(self): 112 | kernel, bias = self.get_equivalent_kernel_bias() 113 | return ( 114 | kernel.detach().cpu().numpy(), 115 | bias.detach().cpu().numpy(), 116 | ) 117 | 118 | def fuse_conv_bn(self, conv, bn): 119 | std = (bn.running_var + bn.eps).sqrt() 120 | bias = bn.bias - bn.running_mean * bn.weight / std 121 | 122 | t = (bn.weight / std).reshape(-1, 1, 1, 1) 123 | weights = conv.weight * t 124 | 125 | bn = nn.Identity() 126 | conv = nn.Conv2d(in_channels = conv.in_channels, 127 | out_channels = conv.out_channels, 128 | kernel_size = conv.kernel_size, 129 | stride=conv.stride, 130 | padding = conv.padding, 131 | dilation = conv.dilation, 132 | groups = conv.groups, 133 | bias = True, 134 | padding_mode = conv.padding_mode) 135 | 136 | conv.weight = torch.nn.Parameter(weights) 137 | conv.bias = torch.nn.Parameter(bias) 138 | return conv 139 | 140 | def fuse_repvgg_block(self): 141 | if self.deploy: 142 | return 143 | print(f"RepConv.fuse_repvgg_block") 144 | self.rbr_dense = self.fuse_conv_bn(self.rbr_dense[0], self.rbr_dense[1]) 145 | 146 | self.rbr_1x1 = self.fuse_conv_bn(self.rbr_1x1[0], self.rbr_1x1[1]) 147 | rbr_1x1_bias = self.rbr_1x1.bias 148 | weight_1x1_expanded = torch.nn.functional.pad(self.rbr_1x1.weight, [1, 1, 1, 1]) 149 | 150 | # Fuse self.rbr_identity 151 | if (isinstance(self.rbr_identity, nn.BatchNorm2d) or isinstance(self.rbr_identity, nn.modules.batchnorm.SyncBatchNorm)): 152 | identity_conv_1x1 = nn.Conv2d( 153 | in_channels=self.in_channels, 154 | out_channels=self.out_channels, 155 | kernel_size=1, 156 | stride=1, 157 | padding=0, 158 | groups=self.groups, 159 | bias=False) 160 | identity_conv_1x1.weight.data = identity_conv_1x1.weight.data.to(self.rbr_1x1.weight.data.device) 161 | identity_conv_1x1.weight.data = identity_conv_1x1.weight.data.squeeze().squeeze() 162 | identity_conv_1x1.weight.data.fill_(0.0) 163 | identity_conv_1x1.weight.data.fill_diagonal_(1.0) 164 | identity_conv_1x1.weight.data = identity_conv_1x1.weight.data.unsqueeze(2).unsqueeze(3) 165 | 166 | identity_conv_1x1 = self.fuse_conv_bn(identity_conv_1x1, self.rbr_identity) 167 | bias_identity_expanded = identity_conv_1x1.bias 168 | weight_identity_expanded = torch.nn.functional.pad(identity_conv_1x1.weight, [1, 1, 1, 1]) 169 | else: 170 | bias_identity_expanded = torch.nn.Parameter( torch.zeros_like(rbr_1x1_bias) ) 171 | weight_identity_expanded = torch.nn.Parameter( torch.zeros_like(weight_1x1_expanded) ) 172 | 173 | self.rbr_dense.weight = torch.nn.Parameter(self.rbr_dense.weight + weight_1x1_expanded + weight_identity_expanded) 174 | self.rbr_dense.bias = torch.nn.Parameter(self.rbr_dense.bias + rbr_1x1_bias + bias_identity_expanded) 175 | 176 | self.rbr_reparam = self.rbr_dense 177 | self.deploy = True 178 | 179 | if self.rbr_identity is not None: 180 | del self.rbr_identity 181 | self.rbr_identity = None 182 | 183 | if self.rbr_1x1 is not None: 184 | del self.rbr_1x1 185 | self.rbr_1x1 = None 186 | 187 | if self.rbr_dense is not None: 188 | del self.rbr_dense 189 | self.rbr_dense = None 190 | 191 | def fuse_conv_and_bn(conv, bn): 192 | fusedconv = nn.Conv2d(conv.in_channels, 193 | conv.out_channels, 194 | kernel_size=conv.kernel_size, 195 | stride=conv.stride, 196 | padding=conv.padding, 197 | groups=conv.groups, 198 | bias=True).requires_grad_(False).to(conv.weight.device) 199 | 200 | w_conv = conv.weight.clone().view(conv.out_channels, -1) 201 | w_bn = torch.diag(bn.weight.div(torch.sqrt(bn.eps + bn.running_var))) 202 | fusedconv.weight.copy_(torch.mm(w_bn, w_conv).view(fusedconv.weight.shape)) 203 | 204 | b_conv = torch.zeros(conv.weight.size(0), device=conv.weight.device) if conv.bias is None else conv.bias 205 | b_bn = bn.bias - bn.weight.mul(bn.running_mean).div(torch.sqrt(bn.running_var + bn.eps)) 206 | fusedconv.bias.copy_(torch.mm(w_bn, b_conv.reshape(-1, 1)).reshape(-1) + b_bn) 207 | return fusedconv 208 | 209 | #---------------------------------------------------# 210 | # yolo_body 211 | #---------------------------------------------------# 212 | class YoloBody(nn.Module): 213 | def __init__(self, anchors_mask, num_classes, phi, pretrained=False): 214 | super(YoloBody, self).__init__() 215 | #-----------------------------------------------# 216 | # 定义了不同yolov7版本的参数 217 | #-----------------------------------------------# 218 | transition_channels = {'l' : 32, 'x' : 40}[phi] 219 | block_channels = 32 220 | panet_channels = {'l' : 32, 'x' : 64}[phi] 221 | e = {'l' : 2, 'x' : 1}[phi] 222 | n = {'l' : 4, 'x' : 6}[phi] 223 | ids = {'l' : [-1, -2, -3, -4, -5, -6], 'x' : [-1, -3, -5, -7, -8]}[phi] 224 | conv = {'l' : RepConv, 'x' : Conv}[phi] 225 | #-----------------------------------------------# 226 | # 输入图片是640, 640, 3 227 | #-----------------------------------------------# 228 | 229 | #---------------------------------------------------# 230 | # 生成主干模型 231 | # 获得三个有效特征层,他们的shape分别是: 232 | # 80, 80, 512 233 | # 40, 40, 1024 234 | # 20, 20, 1024 235 | #---------------------------------------------------# 236 | self.backbone = Backbone(transition_channels, block_channels, n, phi, pretrained=pretrained) 237 | 238 | self.upsample = nn.Upsample(scale_factor=2, mode="nearest") 239 | 240 | self.sppcspc = SPPCSPC(transition_channels * 32, transition_channels * 16) 241 | self.conv_for_P5 = Conv(transition_channels * 16, transition_channels * 8) 242 | self.conv_for_feat2 = Conv(transition_channels * 32, transition_channels * 8) 243 | self.conv3_for_upsample1 = Multi_Concat_Block(transition_channels * 16, panet_channels * 4, transition_channels * 8, e=e, n=n, ids=ids) 244 | 245 | self.conv_for_P4 = Conv(transition_channels * 8, transition_channels * 4) 246 | self.conv_for_feat1 = Conv(transition_channels * 16, transition_channels * 4) 247 | self.conv3_for_upsample2 = Multi_Concat_Block(transition_channels * 8, panet_channels * 2, transition_channels * 4, e=e, n=n, ids=ids) 248 | 249 | self.down_sample1 = Transition_Block(transition_channels * 4, transition_channels * 4) 250 | self.conv3_for_downsample1 = Multi_Concat_Block(transition_channels * 16, panet_channels * 4, transition_channels * 8, e=e, n=n, ids=ids) 251 | 252 | self.down_sample2 = Transition_Block(transition_channels * 8, transition_channels * 8) 253 | self.conv3_for_downsample2 = Multi_Concat_Block(transition_channels * 32, panet_channels * 8, transition_channels * 16, e=e, n=n, ids=ids) 254 | 255 | self.rep_conv_1 = conv(transition_channels * 4, transition_channels * 8, 3, 1) 256 | self.rep_conv_2 = conv(transition_channels * 8, transition_channels * 16, 3, 1) 257 | self.rep_conv_3 = conv(transition_channels * 16, transition_channels * 32, 3, 1) 258 | 259 | self.yolo_head_P3 = nn.Conv2d(transition_channels * 8, len(anchors_mask[2]) * (5 + num_classes), 1) 260 | self.yolo_head_P4 = nn.Conv2d(transition_channels * 16, len(anchors_mask[1]) * (5 + num_classes), 1) 261 | self.yolo_head_P5 = nn.Conv2d(transition_channels * 32, len(anchors_mask[0]) * (5 + num_classes), 1) 262 | 263 | def fuse(self): 264 | print('Fusing layers... ') 265 | for m in self.modules(): 266 | if isinstance(m, RepConv): 267 | m.fuse_repvgg_block() 268 | elif type(m) is Conv and hasattr(m, 'bn'): 269 | m.conv = fuse_conv_and_bn(m.conv, m.bn) 270 | delattr(m, 'bn') 271 | m.forward = m.fuseforward 272 | return self 273 | 274 | def forward(self, x): 275 | # backbone 276 | feat1, feat2, feat3 = self.backbone.forward(x) 277 | 278 | P5 = self.sppcspc(feat3) 279 | P5_conv = self.conv_for_P5(P5) 280 | P5_upsample = self.upsample(P5_conv) 281 | P4 = torch.cat([self.conv_for_feat2(feat2), P5_upsample], 1) 282 | P4 = self.conv3_for_upsample1(P4) 283 | 284 | P4_conv = self.conv_for_P4(P4) 285 | P4_upsample = self.upsample(P4_conv) 286 | P3 = torch.cat([self.conv_for_feat1(feat1), P4_upsample], 1) 287 | P3 = self.conv3_for_upsample2(P3) 288 | 289 | P3_downsample = self.down_sample1(P3) 290 | P4 = torch.cat([P3_downsample, P4], 1) 291 | P4 = self.conv3_for_downsample1(P4) 292 | 293 | P4_downsample = self.down_sample2(P4) 294 | P5 = torch.cat([P4_downsample, P5], 1) 295 | P5 = self.conv3_for_downsample2(P5) 296 | 297 | P3 = self.rep_conv_1(P3) 298 | P4 = self.rep_conv_2(P4) 299 | P5 = self.rep_conv_3(P5) 300 | #---------------------------------------------------# 301 | # 第三个特征层 302 | # y3=(batch_size, 75, 80, 80) 303 | #---------------------------------------------------# 304 | out2 = self.yolo_head_P3(P3) 305 | #---------------------------------------------------# 306 | # 第二个特征层 307 | # y2=(batch_size, 75, 40, 40) 308 | #---------------------------------------------------# 309 | out1 = self.yolo_head_P4(P4) 310 | #---------------------------------------------------# 311 | # 第一个特征层 312 | # y1=(batch_size, 75, 20, 20) 313 | #---------------------------------------------------# 314 | out0 = self.yolo_head_P5(P5) 315 | 316 | return [out0, out1, out2] 317 | -------------------------------------------------------------------------------- /Yolov7/utils/__pycache__/utils.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joecoss/Lane-detection/4ebe7db47bea6eb07548ca5c8b4678fcfdc1a513/Yolov7/utils/__pycache__/utils.cpython-38.pyc -------------------------------------------------------------------------------- /Yolov7/utils/__pycache__/utils_bbox.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joecoss/Lane-detection/4ebe7db47bea6eb07548ca5c8b4678fcfdc1a513/Yolov7/utils/__pycache__/utils_bbox.cpython-38.pyc -------------------------------------------------------------------------------- /Yolov7/utils/utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from PIL import Image 3 | 4 | 5 | #---------------------------------------------------------# 6 | # 将图像转换成RGB图像,防止灰度图在预测时报错。 7 | # 代码仅仅支持RGB图像的预测,所有其它类型的图像都会转化成RGB 8 | #---------------------------------------------------------# 9 | def cvtColor(image): 10 | if len(np.shape(image)) == 3 and np.shape(image)[2] == 3: 11 | return image 12 | else: 13 | image = image.convert('RGB') 14 | return image 15 | 16 | #---------------------------------------------------# 17 | # 对输入图像进行resize 18 | #---------------------------------------------------# 19 | def resize_image(image, size, letterbox_image): 20 | iw, ih = image.size 21 | w, h = size 22 | if letterbox_image: 23 | scale = min(w/iw, h/ih) 24 | nw = int(iw*scale) 25 | nh = int(ih*scale) 26 | 27 | image = image.resize((nw,nh), Image.BICUBIC) 28 | new_image = Image.new('RGB', size, (128,128,128)) 29 | new_image.paste(image, ((w-nw)//2, (h-nh)//2)) 30 | else: 31 | new_image = image.resize((w, h), Image.BICUBIC) 32 | return new_image 33 | 34 | #---------------------------------------------------# 35 | # 获得类 36 | #---------------------------------------------------# 37 | def get_classes(classes_path): 38 | with open(classes_path, encoding='utf-8') as f: 39 | class_names = f.readlines() 40 | class_names = [c.strip() for c in class_names] 41 | return class_names, len(class_names) 42 | 43 | #---------------------------------------------------# 44 | # 获得先验框 45 | #---------------------------------------------------# 46 | def get_anchors(anchors_path): 47 | '''loads the anchors from a file''' 48 | with open(anchors_path, encoding='utf-8') as f: 49 | anchors = f.readline() 50 | anchors = [float(x) for x in anchors.split(',')] 51 | anchors = np.array(anchors).reshape(-1, 2) 52 | return anchors, len(anchors) 53 | 54 | #---------------------------------------------------# 55 | # 获得学习率 56 | #---------------------------------------------------# 57 | def get_lr(optimizer): 58 | for param_group in optimizer.param_groups: 59 | return param_group['lr'] 60 | 61 | def preprocess_input(image): 62 | image /= 255.0 63 | return image 64 | 65 | def show_config(**kwargs): 66 | print('Configurations:') 67 | print('-' * 70) 68 | print('|%25s | %40s|' % ('keys', 'values')) 69 | print('-' * 70) 70 | for key, value in kwargs.items(): 71 | print('|%25s | %40s|' % (str(key), str(value))) 72 | print('-' * 70) 73 | 74 | def download_weights(phi, model_dir="./model_data"): 75 | import os 76 | from torch.hub import load_state_dict_from_url 77 | 78 | download_urls = { 79 | "l" : 'https://github.com/bubbliiiing/yolov7-pytorch/releases/download/v1.0/yolov7_backbone_weights.pth', 80 | "x" : 'https://github.com/bubbliiiing/yolov7-pytorch/releases/download/v1.0/yolov7_x_backbone_weights.pth', 81 | } 82 | url = download_urls[phi] 83 | 84 | if not os.path.exists(model_dir): 85 | os.makedirs(model_dir) 86 | load_state_dict_from_url(url, model_dir) -------------------------------------------------------------------------------- /Yolov7/utils/utils_bbox.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import torch 3 | from torchvision.ops import nms 4 | 5 | 6 | class DecodeBox(): 7 | def __init__(self, anchors, num_classes, input_shape, anchors_mask = [[6,7,8], [3,4,5], [0,1,2]]): 8 | super(DecodeBox, self).__init__() 9 | self.anchors = anchors 10 | self.num_classes = num_classes 11 | self.bbox_attrs = 5 + num_classes 12 | self.input_shape = input_shape 13 | #-----------------------------------------------------------# 14 | # 13x13的特征层对应的anchor是[142, 110],[192, 243],[459, 401] 15 | # 26x26的特征层对应的anchor是[36, 75],[76, 55],[72, 146] 16 | # 52x52的特征层对应的anchor是[12, 16],[19, 36],[40, 28] 17 | #-----------------------------------------------------------# 18 | self.anchors_mask = anchors_mask 19 | 20 | def decode_box(self, inputs): 21 | outputs = [] 22 | for i, input in enumerate(inputs): 23 | #-----------------------------------------------# 24 | # 输入的input一共有三个,他们的shape分别是 25 | # batch_size = 1 26 | # batch_size, 3 * (4 + 1 + 80), 20, 20 27 | # batch_size, 255, 40, 40 28 | # batch_size, 255, 80, 80 29 | #-----------------------------------------------# 30 | batch_size = input.size(0) 31 | input_height = input.size(2) 32 | input_width = input.size(3) 33 | 34 | #-----------------------------------------------# 35 | # 输入为640x640时 36 | # stride_h = stride_w = 32、16、8 37 | #-----------------------------------------------# 38 | stride_h = self.input_shape[0] / input_height 39 | stride_w = self.input_shape[1] / input_width 40 | #-------------------------------------------------# 41 | # 此时获得的scaled_anchors大小是相对于特征层的 42 | #-------------------------------------------------# 43 | scaled_anchors = [(anchor_width / stride_w, anchor_height / stride_h) for anchor_width, anchor_height in self.anchors[self.anchors_mask[i]]] 44 | 45 | #-----------------------------------------------# 46 | # 输入的input一共有三个,他们的shape分别是 47 | # batch_size, 3, 20, 20, 85 48 | # batch_size, 3, 40, 40, 85 49 | # batch_size, 3, 80, 80, 85 50 | #-----------------------------------------------# 51 | prediction = input.view(batch_size, len(self.anchors_mask[i]), 52 | self.bbox_attrs, input_height, input_width).permute(0, 1, 3, 4, 2).contiguous() 53 | 54 | #-----------------------------------------------# 55 | # 先验框的中心位置的调整参数 56 | #-----------------------------------------------# 57 | x = torch.sigmoid(prediction[..., 0]) 58 | y = torch.sigmoid(prediction[..., 1]) 59 | #-----------------------------------------------# 60 | # 先验框的宽高调整参数 61 | #-----------------------------------------------# 62 | w = torch.sigmoid(prediction[..., 2]) 63 | h = torch.sigmoid(prediction[..., 3]) 64 | #-----------------------------------------------# 65 | # 获得置信度,是否有物体 66 | #-----------------------------------------------# 67 | conf = torch.sigmoid(prediction[..., 4]) 68 | #-----------------------------------------------# 69 | # 种类置信度 70 | #-----------------------------------------------# 71 | pred_cls = torch.sigmoid(prediction[..., 5:]) 72 | 73 | FloatTensor = torch.cuda.FloatTensor if x.is_cuda else torch.FloatTensor 74 | LongTensor = torch.cuda.LongTensor if x.is_cuda else torch.LongTensor 75 | 76 | #----------------------------------------------------------# 77 | # 生成网格,先验框中心,网格左上角 78 | # batch_size,3,20,20 79 | #----------------------------------------------------------# 80 | grid_x = torch.linspace(0, input_width - 1, input_width).repeat(input_height, 1).repeat( 81 | batch_size * len(self.anchors_mask[i]), 1, 1).view(x.shape).type(FloatTensor) 82 | grid_y = torch.linspace(0, input_height - 1, input_height).repeat(input_width, 1).t().repeat( 83 | batch_size * len(self.anchors_mask[i]), 1, 1).view(y.shape).type(FloatTensor) 84 | 85 | #----------------------------------------------------------# 86 | # 按照网格格式生成先验框的宽高 87 | # batch_size,3,20,20 88 | #----------------------------------------------------------# 89 | anchor_w = FloatTensor(scaled_anchors).index_select(1, LongTensor([0])) 90 | anchor_h = FloatTensor(scaled_anchors).index_select(1, LongTensor([1])) 91 | anchor_w = anchor_w.repeat(batch_size, 1).repeat(1, 1, input_height * input_width).view(w.shape) 92 | anchor_h = anchor_h.repeat(batch_size, 1).repeat(1, 1, input_height * input_width).view(h.shape) 93 | 94 | #----------------------------------------------------------# 95 | # 利用预测结果对先验框进行调整 96 | # 首先调整先验框的中心,从先验框中心向右下角偏移 97 | # 再调整先验框的宽高。 98 | # x 0 ~ 1 => 0 ~ 2 => -0.5, 1.5 => 负责一定范围的目标的预测 99 | # y 0 ~ 1 => 0 ~ 2 => -0.5, 1.5 => 负责一定范围的目标的预测 100 | # w 0 ~ 1 => 0 ~ 2 => 0 ~ 4 => 先验框的宽高调节范围为0~4倍 101 | # h 0 ~ 1 => 0 ~ 2 => 0 ~ 4 => 先验框的宽高调节范围为0~4倍 102 | #----------------------------------------------------------# 103 | pred_boxes = FloatTensor(prediction[..., :4].shape) 104 | pred_boxes[..., 0] = x.data * 2. - 0.5 + grid_x 105 | pred_boxes[..., 1] = y.data * 2. - 0.5 + grid_y 106 | pred_boxes[..., 2] = (w.data * 2) ** 2 * anchor_w 107 | pred_boxes[..., 3] = (h.data * 2) ** 2 * anchor_h 108 | 109 | #----------------------------------------------------------# 110 | # 将输出结果归一化成小数的形式 111 | #----------------------------------------------------------# 112 | _scale = torch.Tensor([input_width, input_height, input_width, input_height]).type(FloatTensor) 113 | output = torch.cat((pred_boxes.view(batch_size, -1, 4) / _scale, 114 | conf.view(batch_size, -1, 1), pred_cls.view(batch_size, -1, self.num_classes)), -1) 115 | outputs.append(output.data) 116 | return outputs 117 | 118 | def yolo_correct_boxes(self, box_xy, box_wh, input_shape, image_shape, letterbox_image): 119 | #-----------------------------------------------------------------# 120 | # 把y轴放前面是因为方便预测框和图像的宽高进行相乘 121 | #-----------------------------------------------------------------# 122 | box_yx = box_xy[..., ::-1] 123 | box_hw = box_wh[..., ::-1] 124 | input_shape = np.array(input_shape) 125 | image_shape = np.array(image_shape) 126 | 127 | if letterbox_image: 128 | #-----------------------------------------------------------------# 129 | # 这里求出来的offset是图像有效区域相对于图像左上角的偏移情况 130 | # new_shape指的是宽高缩放情况 131 | #-----------------------------------------------------------------# 132 | new_shape = np.round(image_shape * np.min(input_shape/image_shape)) 133 | offset = (input_shape - new_shape)/2./input_shape 134 | scale = input_shape/new_shape 135 | 136 | box_yx = (box_yx - offset) * scale 137 | box_hw *= scale 138 | 139 | box_mins = box_yx - (box_hw / 2.) 140 | box_maxes = box_yx + (box_hw / 2.) 141 | boxes = np.concatenate([box_mins[..., 0:1], box_mins[..., 1:2], box_maxes[..., 0:1], box_maxes[..., 1:2]], axis=-1) 142 | boxes *= np.concatenate([image_shape, image_shape], axis=-1) 143 | return boxes 144 | 145 | def non_max_suppression(self, prediction, num_classes, input_shape, image_shape, letterbox_image, conf_thres=0.5, nms_thres=0.4): 146 | #----------------------------------------------------------# 147 | # 将预测结果的格式转换成左上角右下角的格式。 148 | # prediction [batch_size, num_anchors, 85] 149 | #----------------------------------------------------------# 150 | box_corner = prediction.new(prediction.shape) 151 | box_corner[:, :, 0] = prediction[:, :, 0] - prediction[:, :, 2] / 2 152 | box_corner[:, :, 1] = prediction[:, :, 1] - prediction[:, :, 3] / 2 153 | box_corner[:, :, 2] = prediction[:, :, 0] + prediction[:, :, 2] / 2 154 | box_corner[:, :, 3] = prediction[:, :, 1] + prediction[:, :, 3] / 2 155 | prediction[:, :, :4] = box_corner[:, :, :4] 156 | 157 | output = [None for _ in range(len(prediction))] 158 | for i, image_pred in enumerate(prediction): 159 | #----------------------------------------------------------# 160 | # 对种类预测部分取max。 161 | # class_conf [num_anchors, 1] 种类置信度 162 | # class_pred [num_anchors, 1] 种类 163 | #----------------------------------------------------------# 164 | class_conf, class_pred = torch.max(image_pred[:, 5:5 + num_classes], 1, keepdim=True) 165 | 166 | #----------------------------------------------------------# 167 | # 利用置信度进行第一轮筛选 168 | #----------------------------------------------------------# 169 | conf_mask = (image_pred[:, 4] * class_conf[:, 0] >= conf_thres).squeeze() 170 | 171 | #----------------------------------------------------------# 172 | # 根据置信度进行预测结果的筛选 173 | #----------------------------------------------------------# 174 | image_pred = image_pred[conf_mask] 175 | class_conf = class_conf[conf_mask] 176 | class_pred = class_pred[conf_mask] 177 | if not image_pred.size(0): 178 | continue 179 | #-------------------------------------------------------------------------# 180 | # detections [num_anchors, 7] 181 | # 7的内容为:x1, y1, x2, y2, obj_conf, class_conf, class_pred 182 | #-------------------------------------------------------------------------# 183 | detections = torch.cat((image_pred[:, :5], class_conf.float(), class_pred.float()), 1) 184 | 185 | #------------------------------------------# 186 | # 获得预测结果中包含的所有种类 187 | #------------------------------------------# 188 | unique_labels = detections[:, -1].cpu().unique() 189 | 190 | if prediction.is_cuda: 191 | unique_labels = unique_labels.cuda() 192 | detections = detections.cuda() 193 | 194 | for c in unique_labels: 195 | #------------------------------------------# 196 | # 获得某一类得分筛选后全部的预测结果 197 | #------------------------------------------# 198 | detections_class = detections[detections[:, -1] == c] 199 | 200 | #------------------------------------------# 201 | # 使用官方自带的非极大抑制会速度更快一些! 202 | # 筛选出一定区域内,属于同一种类得分最大的框 203 | #------------------------------------------# 204 | keep = nms( 205 | detections_class[:, :4], 206 | detections_class[:, 4] * detections_class[:, 5], 207 | nms_thres 208 | ) 209 | max_detections = detections_class[keep] 210 | 211 | # # 按照存在物体的置信度排序 212 | # _, conf_sort_index = torch.sort(detections_class[:, 4]*detections_class[:, 5], descending=True) 213 | # detections_class = detections_class[conf_sort_index] 214 | # # 进行非极大抑制 215 | # max_detections = [] 216 | # while detections_class.size(0): 217 | # # 取出这一类置信度最高的,一步一步往下判断,判断重合程度是否大于nms_thres,如果是则去除掉 218 | # max_detections.append(detections_class[0].unsqueeze(0)) 219 | # if len(detections_class) == 1: 220 | # break 221 | # ious = bbox_iou(max_detections[-1], detections_class[1:]) 222 | # detections_class = detections_class[1:][ious < nms_thres] 223 | # # 堆叠 224 | # max_detections = torch.cat(max_detections).data 225 | 226 | # Add max detections to outputs 227 | output[i] = max_detections if output[i] is None else torch.cat((output[i], max_detections)) 228 | 229 | if output[i] is not None: 230 | output[i] = output[i].cpu().numpy() 231 | box_xy, box_wh = (output[i][:, 0:2] + output[i][:, 2:4])/2, output[i][:, 2:4] - output[i][:, 0:2] 232 | output[i][:, :4] = self.yolo_correct_boxes(box_xy, box_wh, input_shape, image_shape, letterbox_image) 233 | return output 234 | 235 | 236 | if __name__ == "__main__": 237 | import matplotlib.pyplot as plt 238 | import numpy as np 239 | 240 | #---------------------------------------------------# 241 | # 将预测值的每个特征层调成真实值 242 | #---------------------------------------------------# 243 | def get_anchors_and_decode(input, input_shape, anchors, anchors_mask, num_classes): 244 | #-----------------------------------------------# 245 | # input batch_size, 3 * (4 + 1 + num_classes), 20, 20 246 | #-----------------------------------------------# 247 | batch_size = input.size(0) 248 | input_height = input.size(2) 249 | input_width = input.size(3) 250 | 251 | #-----------------------------------------------# 252 | # 输入为640x640时 input_shape = [640, 640] input_height = 20, input_width = 20 253 | # 640 / 20 = 32 254 | # stride_h = stride_w = 32 255 | #-----------------------------------------------# 256 | stride_h = input_shape[0] / input_height 257 | stride_w = input_shape[1] / input_width 258 | #-------------------------------------------------# 259 | # 此时获得的scaled_anchors大小是相对于特征层的 260 | # anchor_width, anchor_height / stride_h, stride_w 261 | #-------------------------------------------------# 262 | scaled_anchors = [(anchor_width / stride_w, anchor_height / stride_h) for anchor_width, anchor_height in anchors[anchors_mask[2]]] 263 | 264 | #-----------------------------------------------# 265 | # batch_size, 3 * (4 + 1 + num_classes), 20, 20 => 266 | # batch_size, 3, 5 + num_classes, 20, 20 => 267 | # batch_size, 3, 20, 20, 4 + 1 + num_classes 268 | #-----------------------------------------------# 269 | prediction = input.view(batch_size, len(anchors_mask[2]), 270 | num_classes + 5, input_height, input_width).permute(0, 1, 3, 4, 2).contiguous() 271 | 272 | #-----------------------------------------------# 273 | # 先验框的中心位置的调整参数 274 | #-----------------------------------------------# 275 | x = torch.sigmoid(prediction[..., 0]) 276 | y = torch.sigmoid(prediction[..., 1]) 277 | #-----------------------------------------------# 278 | # 先验框的宽高调整参数 279 | #-----------------------------------------------# 280 | w = torch.sigmoid(prediction[..., 2]) 281 | h = torch.sigmoid(prediction[..., 3]) 282 | #-----------------------------------------------# 283 | # 获得置信度,是否有物体 0 - 1 284 | #-----------------------------------------------# 285 | conf = torch.sigmoid(prediction[..., 4]) 286 | #-----------------------------------------------# 287 | # 种类置信度 0 - 1 288 | #-----------------------------------------------# 289 | pred_cls = torch.sigmoid(prediction[..., 5:]) 290 | 291 | FloatTensor = torch.cuda.FloatTensor if x.is_cuda else torch.FloatTensor 292 | LongTensor = torch.cuda.LongTensor if x.is_cuda else torch.LongTensor 293 | 294 | #----------------------------------------------------------# 295 | # 生成网格,先验框中心,网格左上角 296 | # batch_size,3,20,20 297 | # range(20) 298 | # [ 299 | # [0, 1, 2, 3 ……, 19], 300 | # [0, 1, 2, 3 ……, 19], 301 | # …… (20次) 302 | # [0, 1, 2, 3 ……, 19] 303 | # ] * (batch_size * 3) 304 | # [batch_size, 3, 20, 20] 305 | # 306 | # [ 307 | # [0, 1, 2, 3 ……, 19], 308 | # [0, 1, 2, 3 ……, 19], 309 | # …… (20次) 310 | # [0, 1, 2, 3 ……, 19] 311 | # ].T * (batch_size * 3) 312 | # [batch_size, 3, 20, 20] 313 | #----------------------------------------------------------# 314 | grid_x = torch.linspace(0, input_width - 1, input_width).repeat(input_height, 1).repeat( 315 | batch_size * len(anchors_mask[2]), 1, 1).view(x.shape).type(FloatTensor) 316 | grid_y = torch.linspace(0, input_height - 1, input_height).repeat(input_width, 1).t().repeat( 317 | batch_size * len(anchors_mask[2]), 1, 1).view(y.shape).type(FloatTensor) 318 | 319 | #----------------------------------------------------------# 320 | # 按照网格格式生成先验框的宽高 321 | # batch_size, 3, 20 * 20 => batch_size, 3, 20, 20 322 | # batch_size, 3, 20 * 20 => batch_size, 3, 20, 20 323 | #----------------------------------------------------------# 324 | anchor_w = FloatTensor(scaled_anchors).index_select(1, LongTensor([0])) 325 | anchor_h = FloatTensor(scaled_anchors).index_select(1, LongTensor([1])) 326 | anchor_w = anchor_w.repeat(batch_size, 1).repeat(1, 1, input_height * input_width).view(w.shape) 327 | anchor_h = anchor_h.repeat(batch_size, 1).repeat(1, 1, input_height * input_width).view(h.shape) 328 | 329 | #----------------------------------------------------------# 330 | # 利用预测结果对先验框进行调整 331 | # 首先调整先验框的中心,从先验框中心向右下角偏移 332 | # 再调整先验框的宽高。 333 | # x 0 ~ 1 => 0 ~ 2 => -0.5 ~ 1.5 + grid_x 334 | # y 0 ~ 1 => 0 ~ 2 => -0.5 ~ 1.5 + grid_y 335 | # w 0 ~ 1 => 0 ~ 2 => 0 ~ 4 * anchor_w 336 | # h 0 ~ 1 => 0 ~ 2 => 0 ~ 4 * anchor_h 337 | #----------------------------------------------------------# 338 | pred_boxes = FloatTensor(prediction[..., :4].shape) 339 | pred_boxes[..., 0] = x.data * 2. - 0.5 + grid_x 340 | pred_boxes[..., 1] = y.data * 2. - 0.5 + grid_y 341 | pred_boxes[..., 2] = (w.data * 2) ** 2 * anchor_w 342 | pred_boxes[..., 3] = (h.data * 2) ** 2 * anchor_h 343 | 344 | point_h = 5 345 | point_w = 5 346 | 347 | box_xy = pred_boxes[..., 0:2].cpu().numpy() * 32 348 | box_wh = pred_boxes[..., 2:4].cpu().numpy() * 32 349 | grid_x = grid_x.cpu().numpy() * 32 350 | grid_y = grid_y.cpu().numpy() * 32 351 | anchor_w = anchor_w.cpu().numpy() * 32 352 | anchor_h = anchor_h.cpu().numpy() * 32 353 | 354 | fig = plt.figure() 355 | ax = fig.add_subplot(121) 356 | from PIL import Image 357 | img = Image.open("img/street.jpg").resize([640, 640]) 358 | plt.imshow(img, alpha=0.5) 359 | plt.ylim(-30, 650) 360 | plt.xlim(-30, 650) 361 | plt.scatter(grid_x, grid_y) 362 | plt.scatter(point_h * 32, point_w * 32, c='black') 363 | plt.gca().invert_yaxis() 364 | 365 | anchor_left = grid_x - anchor_w / 2 366 | anchor_top = grid_y - anchor_h / 2 367 | 368 | rect1 = plt.Rectangle([anchor_left[0, 0, point_h, point_w],anchor_top[0, 0, point_h, point_w]], \ 369 | anchor_w[0, 0, point_h, point_w],anchor_h[0, 0, point_h, point_w],color="r",fill=False) 370 | rect2 = plt.Rectangle([anchor_left[0, 1, point_h, point_w],anchor_top[0, 1, point_h, point_w]], \ 371 | anchor_w[0, 1, point_h, point_w],anchor_h[0, 1, point_h, point_w],color="r",fill=False) 372 | rect3 = plt.Rectangle([anchor_left[0, 2, point_h, point_w],anchor_top[0, 2, point_h, point_w]], \ 373 | anchor_w[0, 2, point_h, point_w],anchor_h[0, 2, point_h, point_w],color="r",fill=False) 374 | 375 | ax.add_patch(rect1) 376 | ax.add_patch(rect2) 377 | ax.add_patch(rect3) 378 | 379 | ax = fig.add_subplot(122) 380 | plt.imshow(img, alpha=0.5) 381 | plt.ylim(-30, 650) 382 | plt.xlim(-30, 650) 383 | plt.scatter(grid_x, grid_y) 384 | plt.scatter(point_h * 32, point_w * 32, c='black') 385 | plt.scatter(box_xy[0, :, point_h, point_w, 0], box_xy[0, :, point_h, point_w, 1], c='r') 386 | plt.gca().invert_yaxis() 387 | 388 | pre_left = box_xy[...,0] - box_wh[...,0] / 2 389 | pre_top = box_xy[...,1] - box_wh[...,1] / 2 390 | 391 | rect1 = plt.Rectangle([pre_left[0, 0, point_h, point_w], pre_top[0, 0, point_h, point_w]],\ 392 | box_wh[0, 0, point_h, point_w,0], box_wh[0, 0, point_h, point_w,1],color="r",fill=False) 393 | rect2 = plt.Rectangle([pre_left[0, 1, point_h, point_w], pre_top[0, 1, point_h, point_w]],\ 394 | box_wh[0, 1, point_h, point_w,0], box_wh[0, 1, point_h, point_w,1],color="r",fill=False) 395 | rect3 = plt.Rectangle([pre_left[0, 2, point_h, point_w], pre_top[0, 2, point_h, point_w]],\ 396 | box_wh[0, 2, point_h, point_w,0], box_wh[0, 2, point_h, point_w,1],color="r",fill=False) 397 | 398 | ax.add_patch(rect1) 399 | ax.add_patch(rect2) 400 | ax.add_patch(rect3) 401 | 402 | plt.show() 403 | # 404 | feat = torch.from_numpy(np.random.normal(0.2, 0.5, [4, 255, 20, 20])).float() 405 | anchors = np.array([[116, 90], [156, 198], [373, 326], [30,61], [62,45], [59,119], [10,13], [16,30], [33,23]]) 406 | anchors_mask = [[6, 7, 8], [3, 4, 5], [0, 1, 2]] 407 | get_anchors_and_decode(feat, [640, 640], anchors, anchors_mask, 80) 408 | -------------------------------------------------------------------------------- /Yolov7/yolo.py: -------------------------------------------------------------------------------- 1 | import colorsys 2 | import os 3 | import numpy as np 4 | import torch 5 | import torch.nn as nn 6 | from PIL import ImageDraw, ImageFont 7 | 8 | from Yolov7.nets.yolo import YoloBody 9 | from Yolov7.utils.utils import (cvtColor, get_anchors, get_classes, preprocess_input, 10 | resize_image, show_config) 11 | from Yolov7.utils.utils_bbox import DecodeBox 12 | 13 | 14 | class YOLO(object): 15 | _defaults = { 16 | #--------------------------------------------------------------------------# 17 | # 使用自己训练好的模型进行预测一定要修改model_path和classes_path! 18 | # model_path指向logs文件夹下的权值文件,classes_path指向model_data下的txt 19 | # 20 | # 训练好后logs文件夹下存在多个权值文件,选择验证集损失较低的即可。 21 | # 验证集损失较低不代表mAP较高,仅代表该权值在验证集上泛化性能较好。 22 | # 如果出现shape不匹配,同时要注意训练时的model_path和classes_path参数的修改 23 | #--------------------------------------------------------------------------# 24 | "model_path" : 'YOLOV7/model_data/ep030-loss0.034-val_loss0.020.pth', 25 | "classes_path" : 'YOLOV7/model_data/voc_classes.txt', 26 | #---------------------------------------------------------------------# 27 | # anchors_path代表先验框对应的txt文件,一般不修改。 28 | # anchors_mask用于帮助代码找到对应的先验框,一般不修改。 29 | #---------------------------------------------------------------------# 30 | "anchors_path" : 'YOLOV7/model_data/yolo_anchors.txt', 31 | "anchors_mask" : [[6, 7, 8], [3, 4, 5], [0, 1, 2]], 32 | #---------------------------------------------------------------------# 33 | # 输入图片的大小,必须为32的倍数。 34 | #---------------------------------------------------------------------# 35 | "input_shape" : [640, 640], 36 | #------------------------------------------------------# 37 | # 所使用到的yolov7的版本,本仓库一共提供两个: 38 | # l : 对应yolov7 39 | # x : 对应yolov7_x 40 | #------------------------------------------------------# 41 | "phi" : 'l', 42 | #---------------------------------------------------------------------# 43 | # 只有得分大于置信度的预测框会被保留下来 44 | #---------------------------------------------------------------------# 45 | "confidence" : 0.5, 46 | #---------------------------------------------------------------------# 47 | # 非极大抑制所用到的nms_iou大小 48 | #---------------------------------------------------------------------# 49 | "nms_iou" : 0.3, 50 | #---------------------------------------------------------------------# 51 | # 该变量用于控制是否使用letterbox_image对输入图像进行不失真的resize, 52 | # 在多次测试后,发现关闭letterbox_image直接resize的效果更好 53 | #---------------------------------------------------------------------# 54 | "letterbox_image" : True, 55 | #-------------------------------# 56 | # 是否使用Cuda 57 | # 没有GPU可以设置成False 58 | #-------------------------------# 59 | "cuda" : True, 60 | } 61 | 62 | @classmethod 63 | def get_defaults(cls, n): 64 | if n in cls._defaults: 65 | return cls._defaults[n] 66 | else: 67 | return "Unrecognized attribute name '" + n + "'" 68 | 69 | #---------------------------------------------------# 70 | # 初始化YOLO 71 | #---------------------------------------------------# 72 | def __init__(self, **kwargs): 73 | self.__dict__.update(self._defaults) 74 | for name, value in kwargs.items(): 75 | setattr(self, name, value) 76 | self._defaults[name] = value 77 | 78 | #---------------------------------------------------# 79 | # 获得种类和先验框的数量 80 | #---------------------------------------------------# 81 | self.class_names, self.num_classes = get_classes(self.classes_path) 82 | self.anchors, self.num_anchors = get_anchors(self.anchors_path) 83 | self.bbox_util = DecodeBox(self.anchors, self.num_classes, (self.input_shape[0], self.input_shape[1]), self.anchors_mask) 84 | 85 | #---------------------------------------------------# 86 | # 画框设置不同的颜色 87 | #---------------------------------------------------# 88 | hsv_tuples = [(x / self.num_classes, 1., 1.) for x in range(self.num_classes)] 89 | self.colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples)) 90 | self.colors = list(map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)), self.colors)) 91 | self.generate() 92 | 93 | show_config(**self._defaults) 94 | 95 | #---------------------------------------------------# 96 | # 生成模型 97 | #---------------------------------------------------# 98 | def generate(self, onnx=False): 99 | #---------------------------------------------------# 100 | # 建立yolo模型,载入yolo模型的权重 101 | #---------------------------------------------------# 102 | self.net = YoloBody(self.anchors_mask, self.num_classes, self.phi) 103 | device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 104 | self.net.load_state_dict(torch.load(self.model_path, map_location=device)) 105 | self.net = self.net.fuse().eval() 106 | print('{} model, and classes loaded.'.format(self.model_path)) 107 | if not onnx: 108 | if self.cuda: 109 | self.net = nn.DataParallel(self.net) 110 | self.net = self.net.cuda() 111 | 112 | #---------------------------------------------------# 113 | # 检测图片 114 | #---------------------------------------------------# 115 | def detect_image(self, image, crop = False, count = False): 116 | #---------------------------------------------------# 117 | # 计算输入图片的高和宽 118 | #---------------------------------------------------# 119 | image_shape = np.array(np.shape(image)[0:2]) 120 | #---------------------------------------------------------# 121 | # 在这里将图像转换成RGB图像,防止灰度图在预测时报错。 122 | # 代码仅仅支持RGB图像的预测,所有其它类型的图像都会转化成RGB 123 | #---------------------------------------------------------# 124 | image = cvtColor(image) 125 | #---------------------------------------------------------# 126 | # 给图像增加灰条,实现不失真的resize 127 | # 也可以直接resize进行识别 128 | #---------------------------------------------------------# 129 | image_data = resize_image(image, (self.input_shape[1], self.input_shape[0]), self.letterbox_image) 130 | #---------------------------------------------------------# 131 | # 添加上batch_size维度 132 | #---------------------------------------------------------# 133 | image_data = np.expand_dims(np.transpose(preprocess_input(np.array(image_data, dtype='float32')), (2, 0, 1)), 0) 134 | 135 | with torch.no_grad(): 136 | images = torch.from_numpy(image_data) 137 | if self.cuda: 138 | images = images.cuda() 139 | #---------------------------------------------------------# 140 | # 将图像输入网络当中进行预测! 141 | #---------------------------------------------------------# 142 | outputs = self.net(images) 143 | outputs = self.bbox_util.decode_box(outputs) 144 | #---------------------------------------------------------# 145 | # 将预测框进行堆叠,然后进行非极大抑制 146 | #---------------------------------------------------------# 147 | results = self.bbox_util.non_max_suppression(torch.cat(outputs, 1), self.num_classes, self.input_shape, 148 | image_shape, self.letterbox_image, conf_thres = self.confidence, nms_thres = self.nms_iou) 149 | 150 | if results[0] is None: 151 | return image 152 | 153 | top_label = np.array(results[0][:, 6], dtype = 'int32') 154 | top_conf = results[0][:, 4] * results[0][:, 5] 155 | top_boxes = results[0][:, :4] 156 | #---------------------------------------------------------# 157 | # 设置字体与边框厚度 158 | #---------------------------------------------------------# 159 | font = ImageFont.truetype(font='model_data/simhei.ttf', size=np.floor(3e-2 * image.size[1] + 0.5).astype('int32')) 160 | thickness = int(max((image.size[0] + image.size[1]) // np.mean(self.input_shape), 1)) 161 | #---------------------------------------------------------# 162 | # 图像绘制 163 | #---------------------------------------------------------# 164 | for i, c in list(enumerate(top_label)): 165 | predicted_class = self.class_names[int(c)] 166 | box = top_boxes[i] 167 | score = top_conf[i] 168 | 169 | top, left, bottom, right = box 170 | 171 | top = max(0, np.floor(top).astype('int32')) 172 | left = max(0, np.floor(left).astype('int32')) 173 | bottom = min(image.size[1], np.floor(bottom).astype('int32')) 174 | right = min(image.size[0], np.floor(right).astype('int32')) 175 | 176 | label = '{} {:.2f}'.format(predicted_class, score) 177 | draw = ImageDraw.Draw(image) 178 | label_size = draw.textsize(label, font) 179 | label = label.encode('utf-8') 180 | print(label, top, left, bottom, right) 181 | 182 | if top - label_size[1] >= 0: 183 | text_origin = np.array([left, top - label_size[1]]) 184 | else: 185 | text_origin = np.array([left, top + 1]) 186 | 187 | for i in range(thickness): 188 | draw.rectangle([left + i, top + i, right - i, bottom - i], outline=self.colors[c]) 189 | draw.rectangle([tuple(text_origin), tuple(text_origin + label_size)], fill=self.colors[c]) 190 | draw.text(text_origin, str(label,'UTF-8'), fill=(0, 0, 0), font=font) 191 | del draw 192 | 193 | return image -------------------------------------------------------------------------------- /deeplabv3/__pycache__/deeplab.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joecoss/Lane-detection/4ebe7db47bea6eb07548ca5c8b4678fcfdc1a513/deeplabv3/__pycache__/deeplab.cpython-38.pyc -------------------------------------------------------------------------------- /deeplabv3/deeplab.py: -------------------------------------------------------------------------------- 1 | import colorsys 2 | import copy 3 | import cv2 4 | import numpy as np 5 | import torch 6 | import torch.nn.functional as F 7 | from PIL import Image 8 | from torch import nn 9 | from deeplabv3.nets.deeplabv3_plus import DeepLab 10 | from deeplabv3.utils.utils import cvtColor, preprocess_input, resize_image, show_config 11 | 12 | 13 | #-----------------------------------------------------------------------------------# 14 | # 使用自己训练好的模型预测需要修改3个参数 15 | # model_path、backbone和num_classes都需要修改! 16 | # 如果出现shape不匹配,一定要注意训练时的model_path、backbone和num_classes的修改 17 | #-----------------------------------------------------------------------------------# 18 | class DeeplabV3(object): 19 | _defaults = { 20 | #-------------------------------------------------------------------# 21 | # model_path指向logs文件夹下的权值文件 22 | # 训练好后logs文件夹下存在多个权值文件,选择验证集损失较低的即可。 23 | # 验证集损失较低不代表miou较高,仅代表该权值在验证集上泛化性能较好。 24 | #-------------------------------------------------------------------# 25 | "model_path" : 'F:/PyCharm_wroks/Euro_Car/deeplabv3/model_data/ep240-loss0.012-val_loss0.011.pth', 26 | #----------------------------------------# 27 | # 所需要区分的类的个数+1 28 | #----------------------------------------# 29 | "num_classes" : 2, 30 | #----------------------------------------# 31 | # 所使用的的主干网络: 32 | # mobilenet 33 | # xception 34 | #----------------------------------------# 35 | "backbone" : "mobilenet", 36 | #----------------------------------------# 37 | # 输入图片的大小 38 | #----------------------------------------# 39 | "input_shape" : [512, 512], 40 | #----------------------------------------# 41 | # 下采样的倍数,一般可选的为8和16 42 | # 与训练时设置的一样即可 43 | #----------------------------------------# 44 | "downsample_factor" : 16, 45 | #-------------------------------------------------# 46 | # mix_type参数用于控制检测结果的可视化方式 47 | # 48 | # mix_type = 0的时候代表原图与生成的图进行混合 49 | # mix_type = 1的时候代表仅保留生成的图 50 | # mix_type = 2的时候代表仅扣去背景,仅保留原图中的目标 51 | #-------------------------------------------------# 52 | "mix_type" : 0, 53 | #-------------------------------# 54 | # 是否使用Cuda 55 | # 没有GPU可以设置成False 56 | #-------------------------------# 57 | "cuda" : True, 58 | } 59 | 60 | #---------------------------------------------------# 61 | # 初始化Deeplab 62 | #---------------------------------------------------# 63 | def __init__(self, **kwargs): 64 | self.__dict__.update(self._defaults) 65 | for name, value in kwargs.items(): 66 | setattr(self, name, value) 67 | #---------------------------------------------------# 68 | # 画框设置不同的颜色 69 | #---------------------------------------------------# 70 | if self.num_classes <= 21: 71 | self.colors = [ (0, 0, 0), (128, 0, 0), (0, 128, 0), (128, 128, 0), (0, 0, 128), (128, 0, 128), (0, 128, 128), 72 | (128, 128, 128), (64, 0, 0), (192, 0, 0), (64, 128, 0), (192, 128, 0), (64, 0, 128), (192, 0, 128), 73 | (64, 128, 128), (192, 128, 128), (0, 64, 0), (128, 64, 0), (0, 192, 0), (128, 192, 0), (0, 64, 128), 74 | (128, 64, 12)] 75 | else: 76 | hsv_tuples = [(x / self.num_classes, 1., 1.) for x in range(self.num_classes)] 77 | self.colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples)) 78 | self.colors = list(map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)), self.colors)) 79 | #---------------------------------------------------# 80 | # 获得模型 81 | #---------------------------------------------------# 82 | self.generate() 83 | 84 | show_config(**self._defaults) 85 | 86 | #---------------------------------------------------# 87 | # 获得所有的分类 88 | #---------------------------------------------------# 89 | def generate(self, onnx=False): 90 | #-------------------------------# 91 | # 载入模型与权值 92 | #-------------------------------# 93 | self.net = DeepLab(num_classes=self.num_classes, backbone=self.backbone, downsample_factor=self.downsample_factor, pretrained=False) 94 | 95 | device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 96 | self.net.load_state_dict(torch.load(self.model_path, map_location=device)) 97 | self.net = self.net.eval() 98 | print('{} model, and classes loaded.'.format(self.model_path)) 99 | if not onnx: 100 | if self.cuda: 101 | self.net = nn.DataParallel(self.net) 102 | self.net = self.net.cuda() 103 | 104 | #---------------------------------------------------# 105 | # 检测图片 106 | #---------------------------------------------------# 107 | def detect_image(self, image, count=False, name_classes=None): 108 | #---------------------------------------------------------# 109 | # 在这里将图像转换成RGB图像,防止灰度图在预测时报错。 110 | # 代码仅仅支持RGB图像的预测,所有其它类型的图像都会转化成RGB 111 | #---------------------------------------------------------# 112 | image = cvtColor(image) 113 | #---------------------------------------------------# 114 | # 对输入图像进行一个备份,后面用于绘图 115 | #---------------------------------------------------# 116 | old_img = copy.deepcopy(image) 117 | orininal_h = np.array(image).shape[0] 118 | orininal_w = np.array(image).shape[1] 119 | #---------------------------------------------------------# 120 | # 给图像增加灰条,实现不失真的resize 121 | # 也可以直接resize进行识别 122 | #---------------------------------------------------------# 123 | image_data, nw, nh = resize_image(image, (self.input_shape[1],self.input_shape[0])) 124 | #---------------------------------------------------------# 125 | # 添加上batch_size维度 126 | #---------------------------------------------------------# 127 | image_data = np.expand_dims(np.transpose(preprocess_input(np.array(image_data, np.float32)), (2, 0, 1)), 0) 128 | 129 | with torch.no_grad(): 130 | images = torch.from_numpy(image_data) 131 | if self.cuda: 132 | images = images.cuda() 133 | 134 | #---------------------------------------------------# 135 | # 图片传入网络进行预测 136 | #---------------------------------------------------# 137 | pr = self.net(images)[0] 138 | #---------------------------------------------------# 139 | # 取出每一个像素点的种类 140 | #---------------------------------------------------# 141 | pr = F.softmax(pr.permute(1,2,0),dim = -1).cpu().numpy() 142 | #--------------------------------------# 143 | # 将灰条部分截取掉 144 | #--------------------------------------# 145 | pr = pr[int((self.input_shape[0] - nh) // 2) : int((self.input_shape[0] - nh) // 2 + nh), \ 146 | int((self.input_shape[1] - nw) // 2) : int((self.input_shape[1] - nw) // 2 + nw)] 147 | #---------------------------------------------------# 148 | # 进行图片的resize 149 | #---------------------------------------------------# 150 | pr = cv2.resize(pr, (orininal_w, orininal_h), interpolation = cv2.INTER_LINEAR) 151 | #---------------------------------------------------# 152 | # 取出每一个像素点的种类 153 | #---------------------------------------------------# 154 | pr = pr.argmax(axis=-1) 155 | 156 | #---------------------------------------------------------# 157 | # 计数 158 | #---------------------------------------------------------# 159 | 160 | if self.mix_type == 0: 161 | # seg_img = np.zeros((np.shape(pr)[0], np.shape(pr)[1], 3)) 162 | # for c in range(self.num_classes): 163 | # seg_img[:, :, 0] += ((pr[:, :] == c ) * self.colors[c][0]).astype('uint8') 164 | # seg_img[:, :, 1] += ((pr[:, :] == c ) * self.colors[c][1]).astype('uint8') 165 | # seg_img[:, :, 2] += ((pr[:, :] == c ) * self.colors[c][2]).astype('uint8') 166 | seg_img = np.reshape(np.array(self.colors, np.uint8)[np.reshape(pr, [-1])], [orininal_h, orininal_w, -1]) 167 | #------------------------------------------------# 168 | # 将新图片转换成Image的形式 169 | #------------------------------------------------# 170 | image = Image.fromarray(np.uint8(seg_img)) 171 | image_gray = cv2.cvtColor(np.array(image), cv2.COLOR_BGR2GRAY) 172 | ret, image_thresh_binary = cv2.threshold(np.array(image_gray), 0, 255, cv2.THRESH_BINARY) 173 | # cv2.imshow("asdsad", image_thresh_binary) 174 | 175 | #------------------------------------------------# 176 | # 将新图与原图及进行混合 177 | #------------------------------------------------# 178 | 179 | image = Image.blend(old_img, image, 0.5) 180 | 181 | elif self.mix_type == 1: 182 | # seg_img = np.zeros((np.shape(pr)[0], np.shape(pr)[1], 3)) 183 | # for c in range(self.num_classes): 184 | # seg_img[:, :, 0] += ((pr[:, :] == c ) * self.colors[c][0]).astype('uint8') 185 | # seg_img[:, :, 1] += ((pr[:, :] == c ) * self.colors[c][1]).astype('uint8') 186 | # seg_img[:, :, 2] += ((pr[:, :] == c ) * self.colors[c][2]).astype('uint8') 187 | seg_img = np.reshape(np.array(self.colors, np.uint8)[np.reshape(pr, [-1])], [orininal_h, orininal_w, -1]) 188 | #------------------------------------------------# 189 | # 将新图片转换成Image的形式 190 | #------------------------------------------------# 191 | image = Image.fromarray(np.uint8(seg_img)) 192 | 193 | elif self.mix_type == 2: 194 | seg_img = (np.expand_dims(pr != 0, -1) * np.array(old_img, np.float32)).astype('uint8') 195 | #------------------------------------------------# 196 | # 将新图片转换成Image的形式 197 | #------------------------------------------------# 198 | image = Image.fromarray(np.uint8(seg_img)) 199 | 200 | # return np.array(image) 201 | return image, image_thresh_binary 202 | -------------------------------------------------------------------------------- /deeplabv3/nets/__pycache__/deeplabv3_plus.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joecoss/Lane-detection/4ebe7db47bea6eb07548ca5c8b4678fcfdc1a513/deeplabv3/nets/__pycache__/deeplabv3_plus.cpython-38.pyc -------------------------------------------------------------------------------- /deeplabv3/nets/__pycache__/mobilenetv2.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joecoss/Lane-detection/4ebe7db47bea6eb07548ca5c8b4678fcfdc1a513/deeplabv3/nets/__pycache__/mobilenetv2.cpython-38.pyc -------------------------------------------------------------------------------- /deeplabv3/nets/__pycache__/xception.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joecoss/Lane-detection/4ebe7db47bea6eb07548ca5c8b4678fcfdc1a513/deeplabv3/nets/__pycache__/xception.cpython-38.pyc -------------------------------------------------------------------------------- /deeplabv3/nets/deeplabv3_plus.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.nn.functional as F 4 | from deeplabv3.nets.xception import xception 5 | from deeplabv3.nets.mobilenetv2 import mobilenetv2 6 | 7 | class MobileNetV2(nn.Module): 8 | def __init__(self, downsample_factor=8, pretrained=True): 9 | super(MobileNetV2, self).__init__() 10 | from functools import partial 11 | 12 | model = mobilenetv2(pretrained) 13 | self.features = model.features[:-1] 14 | 15 | self.total_idx = len(self.features) 16 | self.down_idx = [2, 4, 7, 14] 17 | 18 | if downsample_factor == 8: 19 | for i in range(self.down_idx[-2], self.down_idx[-1]): 20 | self.features[i].apply( 21 | partial(self._nostride_dilate, dilate=2) 22 | ) 23 | for i in range(self.down_idx[-1], self.total_idx): 24 | self.features[i].apply( 25 | partial(self._nostride_dilate, dilate=4) 26 | ) 27 | elif downsample_factor == 16: 28 | for i in range(self.down_idx[-1], self.total_idx): 29 | self.features[i].apply( 30 | partial(self._nostride_dilate, dilate=2) 31 | ) 32 | 33 | def _nostride_dilate(self, m, dilate): 34 | classname = m.__class__.__name__ 35 | if classname.find('Conv') != -1: 36 | if m.stride == (2, 2): 37 | m.stride = (1, 1) 38 | if m.kernel_size == (3, 3): 39 | m.dilation = (dilate//2, dilate//2) 40 | m.padding = (dilate//2, dilate//2) 41 | else: 42 | if m.kernel_size == (3, 3): 43 | m.dilation = (dilate, dilate) 44 | m.padding = (dilate, dilate) 45 | 46 | def forward(self, x): 47 | low_level_features = self.features[:4](x) 48 | x = self.features[4:](low_level_features) 49 | return low_level_features, x 50 | 51 | 52 | #-----------------------------------------# 53 | # ASPP特征提取模块 54 | # 利用不同膨胀率的膨胀卷积进行特征提取 55 | #-----------------------------------------# 56 | class ASPP(nn.Module): 57 | def __init__(self, dim_in, dim_out, rate=1, bn_mom=0.1): 58 | super(ASPP, self).__init__() 59 | self.branch1 = nn.Sequential( 60 | nn.Conv2d(dim_in, dim_out, 1, 1, padding=0, dilation=rate,bias=True), 61 | nn.BatchNorm2d(dim_out, momentum=bn_mom), 62 | nn.ReLU(inplace=True), 63 | ) 64 | self.branch2 = nn.Sequential( 65 | nn.Conv2d(dim_in, dim_out, 3, 1, padding=6*rate, dilation=6*rate, bias=True), 66 | nn.BatchNorm2d(dim_out, momentum=bn_mom), 67 | nn.ReLU(inplace=True), 68 | ) 69 | self.branch3 = nn.Sequential( 70 | nn.Conv2d(dim_in, dim_out, 3, 1, padding=12*rate, dilation=12*rate, bias=True), 71 | nn.BatchNorm2d(dim_out, momentum=bn_mom), 72 | nn.ReLU(inplace=True), 73 | ) 74 | self.branch4 = nn.Sequential( 75 | nn.Conv2d(dim_in, dim_out, 3, 1, padding=18*rate, dilation=18*rate, bias=True), 76 | nn.BatchNorm2d(dim_out, momentum=bn_mom), 77 | nn.ReLU(inplace=True), 78 | ) 79 | self.branch5_conv = nn.Conv2d(dim_in, dim_out, 1, 1, 0,bias=True) 80 | self.branch5_bn = nn.BatchNorm2d(dim_out, momentum=bn_mom) 81 | self.branch5_relu = nn.ReLU(inplace=True) 82 | 83 | self.conv_cat = nn.Sequential( 84 | nn.Conv2d(dim_out*5, dim_out, 1, 1, padding=0,bias=True), 85 | nn.BatchNorm2d(dim_out, momentum=bn_mom), 86 | nn.ReLU(inplace=True), 87 | ) 88 | 89 | def forward(self, x): 90 | [b, c, row, col] = x.size() 91 | #-----------------------------------------# 92 | # 一共五个分支 93 | #-----------------------------------------# 94 | conv1x1 = self.branch1(x) 95 | conv3x3_1 = self.branch2(x) 96 | conv3x3_2 = self.branch3(x) 97 | conv3x3_3 = self.branch4(x) 98 | #-----------------------------------------# 99 | # 第五个分支,全局平均池化+卷积 100 | #-----------------------------------------# 101 | global_feature = torch.mean(x,2,True) 102 | global_feature = torch.mean(global_feature,3,True) 103 | global_feature = self.branch5_conv(global_feature) 104 | global_feature = self.branch5_bn(global_feature) 105 | global_feature = self.branch5_relu(global_feature) 106 | global_feature = F.interpolate(global_feature, (row, col), None, 'bilinear', True) 107 | 108 | #-----------------------------------------# 109 | # 将五个分支的内容堆叠起来 110 | # 然后1x1卷积整合特征。 111 | #-----------------------------------------# 112 | feature_cat = torch.cat([conv1x1, conv3x3_1, conv3x3_2, conv3x3_3, global_feature], dim=1) 113 | result = self.conv_cat(feature_cat) 114 | return result 115 | 116 | class DeepLab(nn.Module): 117 | def __init__(self, num_classes, backbone="mobilenet", pretrained=True, downsample_factor=16): 118 | super(DeepLab, self).__init__() 119 | if backbone=="xception": 120 | #----------------------------------# 121 | # 获得两个特征层 122 | # 浅层特征 [128,128,256] 123 | # 主干部分 [30,30,2048] 124 | #----------------------------------# 125 | self.backbone = xception(downsample_factor=downsample_factor, pretrained=pretrained) 126 | in_channels = 2048 127 | low_level_channels = 256 128 | elif backbone=="mobilenet": 129 | #----------------------------------# 130 | # 获得两个特征层 131 | # 浅层特征 [128,128,24] 132 | # 主干部分 [30,30,320] 133 | #----------------------------------# 134 | self.backbone = MobileNetV2(downsample_factor=downsample_factor, pretrained=pretrained) 135 | in_channels = 320 136 | low_level_channels = 24 137 | else: 138 | raise ValueError('Unsupported backbone - `{}`, Use mobilenet, xception.'.format(backbone)) 139 | 140 | #-----------------------------------------# 141 | # ASPP特征提取模块 142 | # 利用不同膨胀率的膨胀卷积进行特征提取 143 | #-----------------------------------------# 144 | self.aspp = ASPP(dim_in=in_channels, dim_out=256, rate=16//downsample_factor) 145 | 146 | #----------------------------------# 147 | # 浅层特征边 148 | #----------------------------------# 149 | self.shortcut_conv = nn.Sequential( 150 | nn.Conv2d(low_level_channels, 48, 1), 151 | nn.BatchNorm2d(48), 152 | nn.ReLU(inplace=True) 153 | ) 154 | 155 | self.cat_conv = nn.Sequential( 156 | nn.Conv2d(48+256, 256, 3, stride=1, padding=1), 157 | nn.BatchNorm2d(256), 158 | nn.ReLU(inplace=True), 159 | nn.Dropout(0.5), 160 | 161 | nn.Conv2d(256, 256, 3, stride=1, padding=1), 162 | nn.BatchNorm2d(256), 163 | nn.ReLU(inplace=True), 164 | 165 | nn.Dropout(0.1), 166 | ) 167 | self.cls_conv = nn.Conv2d(256, num_classes, 1, stride=1) 168 | 169 | def forward(self, x): 170 | H, W = x.size(2), x.size(3) 171 | #-----------------------------------------# 172 | # 获得两个特征层 173 | # low_level_features: 浅层特征-进行卷积处理 174 | # x : 主干部分-利用ASPP结构进行加强特征提取 175 | #-----------------------------------------# 176 | low_level_features, x = self.backbone(x) 177 | x = self.aspp(x) 178 | low_level_features = self.shortcut_conv(low_level_features) 179 | 180 | #-----------------------------------------# 181 | # 将加强特征边上采样 182 | # 与浅层特征堆叠后利用卷积进行特征提取 183 | #-----------------------------------------# 184 | x = F.interpolate(x, size=(low_level_features.size(2), low_level_features.size(3)), mode='bilinear', align_corners=True) 185 | x = self.cat_conv(torch.cat((x, low_level_features), dim=1)) 186 | x = self.cls_conv(x) 187 | x = F.interpolate(x, size=(H, W), mode='bilinear', align_corners=True) 188 | return x 189 | 190 | -------------------------------------------------------------------------------- /deeplabv3/nets/mobilenetv2.py: -------------------------------------------------------------------------------- 1 | import math 2 | import os 3 | 4 | import torch 5 | import torch.nn as nn 6 | import torch.utils.model_zoo as model_zoo 7 | 8 | BatchNorm2d = nn.BatchNorm2d 9 | 10 | def conv_bn(inp, oup, stride): 11 | return nn.Sequential( 12 | nn.Conv2d(inp, oup, 3, stride, 1, bias=False), 13 | BatchNorm2d(oup), 14 | nn.ReLU6(inplace=True) 15 | ) 16 | 17 | def conv_1x1_bn(inp, oup): 18 | return nn.Sequential( 19 | nn.Conv2d(inp, oup, 1, 1, 0, bias=False), 20 | BatchNorm2d(oup), 21 | nn.ReLU6(inplace=True) 22 | ) 23 | 24 | class InvertedResidual(nn.Module): 25 | def __init__(self, inp, oup, stride, expand_ratio): 26 | super(InvertedResidual, self).__init__() 27 | self.stride = stride 28 | assert stride in [1, 2] 29 | 30 | hidden_dim = round(inp * expand_ratio) 31 | self.use_res_connect = self.stride == 1 and inp == oup 32 | 33 | if expand_ratio == 1: 34 | self.conv = nn.Sequential( 35 | #--------------------------------------------# 36 | # 进行3x3的逐层卷积,进行跨特征点的特征提取 37 | #--------------------------------------------# 38 | nn.Conv2d(hidden_dim, hidden_dim, 3, stride, 1, groups=hidden_dim, bias=False), 39 | BatchNorm2d(hidden_dim), 40 | nn.ReLU6(inplace=True), 41 | #-----------------------------------# 42 | # 利用1x1卷积进行通道数的调整 43 | #-----------------------------------# 44 | nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False), 45 | BatchNorm2d(oup), 46 | ) 47 | else: 48 | self.conv = nn.Sequential( 49 | #-----------------------------------# 50 | # 利用1x1卷积进行通道数的上升 51 | #-----------------------------------# 52 | nn.Conv2d(inp, hidden_dim, 1, 1, 0, bias=False), 53 | BatchNorm2d(hidden_dim), 54 | nn.ReLU6(inplace=True), 55 | #--------------------------------------------# 56 | # 进行3x3的逐层卷积,进行跨特征点的特征提取 57 | #--------------------------------------------# 58 | nn.Conv2d(hidden_dim, hidden_dim, 3, stride, 1, groups=hidden_dim, bias=False), 59 | BatchNorm2d(hidden_dim), 60 | nn.ReLU6(inplace=True), 61 | #-----------------------------------# 62 | # 利用1x1卷积进行通道数的下降 63 | #-----------------------------------# 64 | nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False), 65 | BatchNorm2d(oup), 66 | ) 67 | 68 | def forward(self, x): 69 | if self.use_res_connect: 70 | return x + self.conv(x) 71 | else: 72 | return self.conv(x) 73 | 74 | class MobileNetV2(nn.Module): 75 | def __init__(self, n_class=1000, input_size=224, width_mult=1.): 76 | super(MobileNetV2, self).__init__() 77 | block = InvertedResidual 78 | input_channel = 32 79 | last_channel = 1280 80 | interverted_residual_setting = [ 81 | # t, c, n, s 82 | [1, 16, 1, 1], # 256, 256, 32 -> 256, 256, 16 83 | [6, 24, 2, 2], # 256, 256, 16 -> 128, 128, 24 2 84 | [6, 32, 3, 2], # 128, 128, 24 -> 64, 64, 32 4 85 | [6, 64, 4, 2], # 64, 64, 32 -> 32, 32, 64 7 86 | [6, 96, 3, 1], # 32, 32, 64 -> 32, 32, 96 87 | [6, 160, 3, 2], # 32, 32, 96 -> 16, 16, 160 14 88 | [6, 320, 1, 1], # 16, 16, 160 -> 16, 16, 320 89 | ] 90 | 91 | assert input_size % 32 == 0 92 | input_channel = int(input_channel * width_mult) 93 | self.last_channel = int(last_channel * width_mult) if width_mult > 1.0 else last_channel 94 | # 512, 512, 3 -> 256, 256, 32 95 | self.features = [conv_bn(3, input_channel, 2)] 96 | 97 | for t, c, n, s in interverted_residual_setting: 98 | output_channel = int(c * width_mult) 99 | for i in range(n): 100 | if i == 0: 101 | self.features.append(block(input_channel, output_channel, s, expand_ratio=t)) 102 | else: 103 | self.features.append(block(input_channel, output_channel, 1, expand_ratio=t)) 104 | input_channel = output_channel 105 | 106 | self.features.append(conv_1x1_bn(input_channel, self.last_channel)) 107 | self.features = nn.Sequential(*self.features) 108 | 109 | self.classifier = nn.Sequential( 110 | nn.Dropout(0.2), 111 | nn.Linear(self.last_channel, n_class), 112 | ) 113 | 114 | self._initialize_weights() 115 | 116 | def forward(self, x): 117 | x = self.features(x) 118 | x = x.mean(3).mean(2) 119 | x = self.classifier(x) 120 | return x 121 | 122 | def _initialize_weights(self): 123 | for m in self.modules(): 124 | if isinstance(m, nn.Conv2d): 125 | n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels 126 | m.weight.data.normal_(0, math.sqrt(2. / n)) 127 | if m.bias is not None: 128 | m.bias.data.zero_() 129 | elif isinstance(m, BatchNorm2d): 130 | m.weight.data.fill_(1) 131 | m.bias.data.zero_() 132 | elif isinstance(m, nn.Linear): 133 | n = m.weight.size(1) 134 | m.weight.data.normal_(0, 0.01) 135 | m.bias.data.zero_() 136 | 137 | 138 | def load_url(url, model_dir='./model_data', map_location=None): 139 | if not os.path.exists(model_dir): 140 | os.makedirs(model_dir) 141 | filename = url.split('/')[-1] 142 | cached_file = os.path.join(model_dir, filename) 143 | if os.path.exists(cached_file): 144 | return torch.load(cached_file, map_location=map_location) 145 | else: 146 | return model_zoo.load_url(url,model_dir=model_dir) 147 | 148 | def mobilenetv2(pretrained=False, **kwargs): 149 | model = MobileNetV2(n_class=1000, **kwargs) 150 | if pretrained: 151 | model.load_state_dict(load_url('https://github.com/bubbliiiing/deeplabv3-plus-pytorch/releases/download/v1.0/mobilenet_v2.pth.tar'), strict=False) 152 | return model 153 | 154 | if __name__ == "__main__": 155 | model = mobilenetv2() 156 | for i, layer in enumerate(model.features): 157 | print(i, layer) 158 | -------------------------------------------------------------------------------- /deeplabv3/nets/xception.py: -------------------------------------------------------------------------------- 1 | import math 2 | import os 3 | import torch 4 | import torch.nn as nn 5 | import torch.utils.model_zoo as model_zoo 6 | 7 | bn_mom = 0.0003 8 | 9 | class SeparableConv2d(nn.Module): 10 | def __init__(self,in_channels,out_channels,kernel_size=1,stride=1,padding=0,dilation=1,bias=False,activate_first=True,inplace=True): 11 | super(SeparableConv2d,self).__init__() 12 | self.relu0 = nn.ReLU(inplace=inplace) 13 | self.depthwise = nn.Conv2d(in_channels,in_channels,kernel_size,stride,padding,dilation,groups=in_channels,bias=bias) 14 | self.bn1 = nn.BatchNorm2d(in_channels, momentum=bn_mom) 15 | self.relu1 = nn.ReLU(inplace=True) 16 | self.pointwise = nn.Conv2d(in_channels,out_channels,1,1,0,1,1,bias=bias) 17 | self.bn2 = nn.BatchNorm2d(out_channels, momentum=bn_mom) 18 | self.relu2 = nn.ReLU(inplace=True) 19 | self.activate_first = activate_first 20 | def forward(self,x): 21 | if self.activate_first: 22 | x = self.relu0(x) 23 | x = self.depthwise(x) 24 | x = self.bn1(x) 25 | if not self.activate_first: 26 | x = self.relu1(x) 27 | x = self.pointwise(x) 28 | x = self.bn2(x) 29 | if not self.activate_first: 30 | x = self.relu2(x) 31 | return x 32 | 33 | class Block(nn.Module): 34 | def __init__(self,in_filters,out_filters,strides=1,atrous=None,grow_first=True,activate_first=True,inplace=True): 35 | super(Block, self).__init__() 36 | if atrous == None: 37 | atrous = [1]*3 38 | elif isinstance(atrous, int): 39 | atrous_list = [atrous]*3 40 | atrous = atrous_list 41 | idx = 0 42 | self.head_relu = True 43 | if out_filters != in_filters or strides!=1: 44 | self.skip = nn.Conv2d(in_filters,out_filters,1,stride=strides, bias=False) 45 | self.skipbn = nn.BatchNorm2d(out_filters, momentum=bn_mom) 46 | self.head_relu = False 47 | else: 48 | self.skip=None 49 | 50 | self.hook_layer = None 51 | if grow_first: 52 | filters = out_filters 53 | else: 54 | filters = in_filters 55 | self.sepconv1 = SeparableConv2d(in_filters,filters,3,stride=1,padding=1*atrous[0],dilation=atrous[0],bias=False,activate_first=activate_first,inplace=self.head_relu) 56 | self.sepconv2 = SeparableConv2d(filters,out_filters,3,stride=1,padding=1*atrous[1],dilation=atrous[1],bias=False,activate_first=activate_first) 57 | self.sepconv3 = SeparableConv2d(out_filters,out_filters,3,stride=strides,padding=1*atrous[2],dilation=atrous[2],bias=False,activate_first=activate_first,inplace=inplace) 58 | 59 | def forward(self,inp): 60 | 61 | if self.skip is not None: 62 | skip = self.skip(inp) 63 | skip = self.skipbn(skip) 64 | else: 65 | skip = inp 66 | 67 | x = self.sepconv1(inp) 68 | x = self.sepconv2(x) 69 | self.hook_layer = x 70 | x = self.sepconv3(x) 71 | 72 | x+=skip 73 | return x 74 | 75 | 76 | class Xception(nn.Module): 77 | """ 78 | Xception optimized for the ImageNet dataset, as specified in 79 | https://arxiv.org/pdf/1610.02357.pdf 80 | """ 81 | def __init__(self, downsample_factor): 82 | """ Constructor 83 | Args: 84 | num_classes: number of classes 85 | """ 86 | super(Xception, self).__init__() 87 | 88 | stride_list = None 89 | if downsample_factor == 8: 90 | stride_list = [2,1,1] 91 | elif downsample_factor == 16: 92 | stride_list = [2,2,1] 93 | else: 94 | raise ValueError('xception.py: output stride=%d is not supported.'%os) 95 | self.conv1 = nn.Conv2d(3, 32, 3, 2, 1, bias=False) 96 | self.bn1 = nn.BatchNorm2d(32, momentum=bn_mom) 97 | self.relu = nn.ReLU(inplace=True) 98 | 99 | self.conv2 = nn.Conv2d(32,64,3,1,1,bias=False) 100 | self.bn2 = nn.BatchNorm2d(64, momentum=bn_mom) 101 | #do relu here 102 | 103 | self.block1=Block(64,128,2) 104 | self.block2=Block(128,256,stride_list[0],inplace=False) 105 | self.block3=Block(256,728,stride_list[1]) 106 | 107 | rate = 16//downsample_factor 108 | self.block4=Block(728,728,1,atrous=rate) 109 | self.block5=Block(728,728,1,atrous=rate) 110 | self.block6=Block(728,728,1,atrous=rate) 111 | self.block7=Block(728,728,1,atrous=rate) 112 | 113 | self.block8=Block(728,728,1,atrous=rate) 114 | self.block9=Block(728,728,1,atrous=rate) 115 | self.block10=Block(728,728,1,atrous=rate) 116 | self.block11=Block(728,728,1,atrous=rate) 117 | 118 | self.block12=Block(728,728,1,atrous=rate) 119 | self.block13=Block(728,728,1,atrous=rate) 120 | self.block14=Block(728,728,1,atrous=rate) 121 | self.block15=Block(728,728,1,atrous=rate) 122 | 123 | self.block16=Block(728,728,1,atrous=[1*rate,1*rate,1*rate]) 124 | self.block17=Block(728,728,1,atrous=[1*rate,1*rate,1*rate]) 125 | self.block18=Block(728,728,1,atrous=[1*rate,1*rate,1*rate]) 126 | self.block19=Block(728,728,1,atrous=[1*rate,1*rate,1*rate]) 127 | 128 | self.block20=Block(728,1024,stride_list[2],atrous=rate,grow_first=False) 129 | self.conv3 = SeparableConv2d(1024,1536,3,1,1*rate,dilation=rate,activate_first=False) 130 | 131 | self.conv4 = SeparableConv2d(1536,1536,3,1,1*rate,dilation=rate,activate_first=False) 132 | 133 | self.conv5 = SeparableConv2d(1536,2048,3,1,1*rate,dilation=rate,activate_first=False) 134 | self.layers = [] 135 | 136 | #------- init weights -------- 137 | for m in self.modules(): 138 | if isinstance(m, nn.Conv2d): 139 | n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels 140 | m.weight.data.normal_(0, math.sqrt(2. / n)) 141 | elif isinstance(m, nn.BatchNorm2d): 142 | m.weight.data.fill_(1) 143 | m.bias.data.zero_() 144 | #----------------------------- 145 | 146 | def forward(self, input): 147 | self.layers = [] 148 | x = self.conv1(input) 149 | x = self.bn1(x) 150 | x = self.relu(x) 151 | x = self.conv2(x) 152 | x = self.bn2(x) 153 | x = self.relu(x) 154 | 155 | x = self.block1(x) 156 | x = self.block2(x) 157 | low_featrue_layer = self.block2.hook_layer 158 | x = self.block3(x) 159 | x = self.block4(x) 160 | x = self.block5(x) 161 | x = self.block6(x) 162 | x = self.block7(x) 163 | x = self.block8(x) 164 | x = self.block9(x) 165 | x = self.block10(x) 166 | x = self.block11(x) 167 | x = self.block12(x) 168 | x = self.block13(x) 169 | x = self.block14(x) 170 | x = self.block15(x) 171 | x = self.block16(x) 172 | x = self.block17(x) 173 | x = self.block18(x) 174 | x = self.block19(x) 175 | x = self.block20(x) 176 | 177 | x = self.conv3(x) 178 | 179 | x = self.conv4(x) 180 | 181 | x = self.conv5(x) 182 | return low_featrue_layer,x 183 | 184 | def load_url(url, model_dir='./model_data', map_location=None): 185 | if not os.path.exists(model_dir): 186 | os.makedirs(model_dir) 187 | filename = url.split('/')[-1] 188 | cached_file = os.path.join(model_dir, filename) 189 | if os.path.exists(cached_file): 190 | return torch.load(cached_file, map_location=map_location) 191 | else: 192 | return model_zoo.load_url(url,model_dir=model_dir) 193 | 194 | def xception(pretrained=True, downsample_factor=16): 195 | model = Xception(downsample_factor=downsample_factor) 196 | if pretrained: 197 | model.load_state_dict(load_url('https://github.com/bubbliiiing/deeplabv3-plus-pytorch/releases/download/v1.0/xception_pytorch_imagenet.pth'), strict=False) 198 | return model 199 | -------------------------------------------------------------------------------- /deeplabv3/utils/__pycache__/utils.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joecoss/Lane-detection/4ebe7db47bea6eb07548ca5c8b4678fcfdc1a513/deeplabv3/utils/__pycache__/utils.cpython-38.pyc -------------------------------------------------------------------------------- /deeplabv3/utils/utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from PIL import Image 3 | 4 | #---------------------------------------------------------# 5 | # 将图像转换成RGB图像,防止灰度图在预测时报错。 6 | # 代码仅仅支持RGB图像的预测,所有其它类型的图像都会转化成RGB 7 | #---------------------------------------------------------# 8 | def cvtColor(image): 9 | if len(np.shape(image)) == 3 and np.shape(image)[2] == 3: 10 | return image 11 | else: 12 | image = image.convert('RGB') 13 | return image 14 | 15 | #---------------------------------------------------# 16 | # 对输入图像进行resize 17 | #---------------------------------------------------# 18 | def resize_image(image, size): 19 | iw, ih = image.size 20 | w, h = size 21 | 22 | scale = min(w/iw, h/ih) 23 | nw = int(iw*scale) 24 | nh = int(ih*scale) 25 | 26 | image = image.resize((nw,nh), Image.BICUBIC) 27 | new_image = Image.new('RGB', size, (128,128,128)) 28 | new_image.paste(image, ((w-nw)//2, (h-nh)//2)) 29 | 30 | return new_image, nw, nh 31 | 32 | #---------------------------------------------------# 33 | # 获得学习率 34 | #---------------------------------------------------# 35 | def get_lr(optimizer): 36 | for param_group in optimizer.param_groups: 37 | return param_group['lr'] 38 | 39 | def preprocess_input(image): 40 | image /= 255.0 41 | return image 42 | 43 | def show_config(**kwargs): 44 | print('Configurations:') 45 | print('-' * 70) 46 | print('|%25s | %40s|' % ('keys', 'values')) 47 | print('-' * 70) 48 | for key, value in kwargs.items(): 49 | print('|%25s | %40s|' % (str(key), str(value))) 50 | print('-' * 70) 51 | 52 | def download_weights(backbone, model_dir="./model_data"): 53 | import os 54 | from torch.hub import load_state_dict_from_url 55 | 56 | download_urls = { 57 | 'mobilenet' : 'https://github.com/bubbliiiing/deeplabv3-plus-pytorch/releases/download/v1.0/mobilenet_v2.pth.tar', 58 | 'xception' : 'https://github.com/bubbliiiing/deeplabv3-plus-pytorch/releases/download/v1.0/xception_pytorch_imagenet.pth', 59 | } 60 | url = download_urls[backbone] 61 | 62 | if not os.path.exists(model_dir): 63 | os.makedirs(model_dir) 64 | load_state_dict_from_url(url, model_dir) -------------------------------------------------------------------------------- /image/Road.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joecoss/Lane-detection/4ebe7db47bea6eb07548ca5c8b4678fcfdc1a513/image/Road.jpg -------------------------------------------------------------------------------- /image/night_img1130.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joecoss/Lane-detection/4ebe7db47bea6eb07548ca5c8b4678fcfdc1a513/image/night_img1130.jpg -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from deeplabv3.deeplab import DeeplabV3 2 | from Yolov7.yolo import YOLO 3 | from PIL import Image 4 | import numpy as np 5 | import cv2 6 | from Lane_line.lane import Lane_line 7 | 8 | def Lane_draw(lane, image_yolo, image_thresh_binary): 9 | # 透视变换局部变量 10 | src_corners = [(415, 335), (585, 335), (1000, 600), (0, 600)] 11 | # src_corners = [(430, 335), (560, 335), (1000, 600), (0, 600)] 12 | dst_corners = [(0, 0), (1000, 0), (1000, 600), (0, 600)] 13 | M = cv2.getPerspectiveTransform(np.float32(src_corners), np.float32(dst_corners)) 14 | N = cv2.getPerspectiveTransform(np.float32(dst_corners), np.float32(src_corners)) 15 | # 透视变换成鸟瞰图 16 | aerial_view = lane.perspective_transform(image_thresh_binary, M) 17 | # 获取左右车道线的曲线坐标 18 | left_fit, right_fit, out_img, True_False = lane.find_line_fit(np.array(aerial_view)) 19 | if True_False == 1: 20 | left_fitx, right_fitx, ploty = lane.get_fit_xy(out_img, left_fit, right_fit) 21 | # 绘制车道掩膜鸟瞰图 22 | mask_aerial_view = lane.project_back(np.array(aerial_view), left_fitx, right_fitx, ploty) 23 | result = Lane(lane, mask_aerial_view, np.array(image_yolo), left_fitx, right_fitx, N) 24 | return result 25 | else: 26 | return img 27 | 28 | # 功能坐标的绘制及图像融合 29 | def Lane(lane, mask_aerial_view, image_yolo, pts_left, pts_right,N): 30 | # 将549行的左右曲线点和左右曲线中点的横坐标存放在left_right的列表中 31 | left = np.int_(pts_left[549]) 32 | right = np.int_(pts_right[549]) 33 | left_right = [left, right, int((left+right)/2)] 34 | if len(left_right) > 1: 35 | # 进行功能坐标的绘制 36 | image = cv2.line(mask_aerial_view, (left_right[0], 549), (left_right[1], 549), (255, 0, 0), 3) 37 | image_blue_left = cv2.line(image, (left_right[0], 524), (left_right[0], 574), (255, 0, 0), 5) 38 | image_blue_right = cv2.line(image_blue_left, (left_right[1], 524), (left_right[1], 574), (255, 0, 0), 5) 39 | image_bule_center = cv2.line(image_blue_right, (left_right[2], 534), (left_right[2], 564), (255, 0, 0), 3) 40 | image_white_center = cv2.line(image_bule_center, (500, 524), (500, 574), (255, 255, 255), 5) 41 | if left_right[2] < 480: 42 | ation_text = "left" 43 | mask_aerial_view = cv2.arrowedLine(np.array(image_white_center), (500, 549), (left_right[2], 549), (0, 0, 255), 44 | thickness=3, line_type=cv2.LINE_4, shift=0, tipLength=0.2) 45 | elif left_right[2] > 520: 46 | ation_text = "right" 47 | mask_aerial_view = cv2.arrowedLine(np.array(image_white_center), (500, 549), (left_right[2], 549), (0, 0, 255), 48 | thickness=3, line_type=cv2.LINE_4, shift=0, tipLength=0.2) 49 | else: 50 | ation_text = "center" 51 | mask_aerial_view = cv2.line(np.array(image_white_center), (480, 549), (520, 549), (255, 0, 255), 5) 52 | # 车道掩膜鸟瞰图透视变换回车道掩膜前视图 53 | mask_front_view = lane.perspective_transform(mask_aerial_view, N) 54 | # 图像融合 55 | result = cv2.addWeighted(image_yolo, 1, mask_front_view, 0.7, 0) 56 | # 在图像上绘制转向文本 57 | result = cv2.putText(result, "ation: "+ation_text, (50, 50), cv2.FONT_HERSHEY_DUPLEX, 1, (0, 255, 0), 2) 58 | return result 59 | 60 | if __name__ == '__main__': 61 | yolo = YOLO() 62 | deeplab = DeeplabV3() 63 | lane = Lane_line() 64 | img = cv2.imread("./image/night_img1130.jpg") 65 | img = cv2.resize(img, (1000, 600)) 66 | img = Image.fromarray(np.uint8(img)) 67 | image_yolo = yolo.detect_image(img) 68 | image, image_thresh_binary = deeplab.detect_image(img) 69 | result = Lane_draw(lane, np.array(image_yolo), np.array(image_thresh_binary)) 70 | while True: 71 | cv2.imshow("frame", result) 72 | cv2.waitKey(1) & 0xff --------------------------------------------------------------------------------