当前位置: 首页 > news >正文

车道线检测

前言

目前,车道线检测技术已经相当成熟,主要应用在自动驾驶、智能交通等领域。下面列举一些当下最流行的车道线检测方法:

  • 基于图像处理的车道线检测方法。该方法是通过图像处理技术从摄像头传回的图像中提取车道线信息的一种方法,主要是利用图像处理算法进行车道线的检测和识别,并输出车道线的位置信息。

  • 基于激光雷达的车道线检测方法。该方法通过激光雷达扫描地面,获取车道线位置信息。这种方法对于在光照较弱、天气恶劣的情况下车道线能更加准确地被检测出来。

  • 基于雷达与摄像头的融合车道线检测方法。此方法是将雷达和摄像头两个传感器的数据进行融合,从而得到更加准确的车道线位置信息,检测的鲁棒性也得到了提高。

  • 基于GPS和地图的车道线检测方法。该方法主要是利用车辆上的GPS以及地图数据来检测车道线的位置信息。这种方法可以克服图像处理技术在某些特定情况下(比如光照不足或者环境光线无法反射)的不足。

以上这些方法均存在优缺点,不同方法的选择主要取决于具体的技术需求和场景应用。在该章节以基于图像处理的车道线检测方法进行介绍。分别从基础的入门级方法到深度学习的方法进行介绍。

传统图像方法
传统图像方法通过边缘检测滤波等方式分割出车道线区域,然后结合霍夫变换、RANSAC等算法进行车道线检测。这类算法需要人工手动去调滤波算子,根据算法所针对的街道场景特点手动调节参数曲线,工作量大且鲁棒性较差,当行车环境出现明显变化时,车道线的检测效果不佳。基于道路特征检测根据提取特征不同,分为:基于颜色特征、纹理特征、多特征融合;

例如:在车道图像中,路面与车道线交汇处的灰度值变换剧烈,利用边缘增强算子突出图像的局部边缘,定义像素的边缘强度,设置阈值方法提取边缘点;
常用的算子:Sobel算子、Prewitt算子、Log算子、Canny算子;
基于灰度特征检测结构简单,对于路面平整、车道线清晰的结构化道路尤为适用;但当光照强烈、有大量异物遮挡、道路结构复杂、车道线较为模糊时,检测效果受到很大的影响;

使用openCV的传统算法

  • Canny边缘检测
  • 高斯滤波
  • ROI和mask
  • 霍夫变换

openCV在图片和视频相关常用的代码
在这里插入图片描述

# 读取图片 cv2.imread第一个参数是窗口的名字,第二个参数是读取格式(彩色或灰度)
cv2.imread(const String & filename, int flags = IMREAD_COLOR)#显示图片 cv2.imshow第一个参数是窗口的名字  第二个参数是显示格式,
cv2.imshow(name, img)	#保持图片
cv2.imwrite(newfile_name, img)#关闭所有openCV打开的窗口。
cv2.destroyAllWindows()#-----------------------------#
#打开视频
capture = cv2.VideoCapture('video.mp4')#按帧读取视频 
#capture.read()有两个返回值。其中ret是布尔值,如果读取帧是正确的则返回True,如果文件读取到结尾,它的返回值就为False。frame就是每一帧的图像,是个三维矩阵。
ret, frame = capture.read()#视频编码格式设置
fourcc = cv2.VideoWriter_fourcc('X', 'V', 'I', 'D')"""
补充:cv2.VideoWriter_fourcc(‘I’, ‘4’, ‘2’, ‘0’),该参数是YUV编码类型,文件名后缀为.avi
cv2.VideoWriter_fourcc(‘P’, ‘I’, ‘M’, ‘I’),该参数是MPEG-1编码类型,文件名后缀为.avi
cv2.VideoWriter_fourcc(‘X’, ‘V’, ‘I’, ‘D’),该参数是MPEG-4编码类型,文件名后缀为.avi
cv2.VideoWriter_fourcc(‘T’, ‘H’, ‘E’, ‘O’),该参数是Ogg Vorbis,文件名后缀为.ogv
cv2.VideoWriter_fourcc(‘F’, ‘L’, ‘V’, ‘1’),该参数是Flash视频,文件名后缀为.flv"""

高斯滤波

Canny边缘检测

有关边缘检测也是计算机视觉。首先利用梯度变化来检测图像中的边,如何识别图像的梯度变化呢,答案是卷积核。卷积核是就是不连续的像素上找到梯度变化较大位置。我们知道 sobal 核可以很好检测边缘,那么 canny 就是 sobal 核检测上进行优化。
CV2提供了提取图像边缘的函数canny。其算法思想如下:

  1. 使用高斯模糊,去除噪音点(cv2.GaussianBlur)
  2. 灰度转换(cv2.cvtColor)
  3. 使用sobel算子,计算出每个点的梯度大小和梯度方向
  4. 使用非极大值抑制(只有最大的保留),消除边缘检测带来的杂散效应
  5. 应用双阈值,来确定真实和潜在的边缘
  6. 通过抑制弱边缘来完成最终的边缘检测
#color_img 输入图片
#gaussian_ksize 高斯核大小,可以为方形矩阵,也可以为矩形
#gaussian_sigmax X方向上的高斯核标准偏差
gaussian = cv2.GaussianBlur(color_img, (gaussian_ksize,gaussian_ksize), gaussian_sigmax)#用于颜色空间转换。input_image为需要转换的图片,flag为转换的类型,返回值为颜色空间转换后的图片矩阵。
#flag对应:
#cv2.COLOR_BGR2GRAY BGR -> Gray
#cv2.COLOR_BGR2RGB BGR -> RGB
#cv2.COLOR_BGR2HSV BGR -> HSVgray_img = cv2.cvtColor(input_image, flag)

输出结果:
在这里插入图片描述

#imag为所操作的图片,threshold1为下阈值,threshold2为上阈值,返回值为边缘图。
edge_img = cv2.Canny(gray_img,canny_threshold1,canny_threshold2)#整成canny_edge_detect的方法
def canny_edge_detect(img):gray = cv2.cvtColor(img,cv2.COLOR_RGB2GRAY)kernel_size = 5blur_gray = cv2.GaussianBlur(gray,(kernel_size, kernel_size),0)low_threshold = 180high_threshold = 240edges = cv2.Canny(blur_gray, low_threshold, high_threshold)return edges

在这里插入图片描述

ROI and mask

在机器视觉或图像处理中,通过图像获取到的信息通常是一个二维数组或矩阵,这些信息中可能包含需要进一步处理的区域以及不需要处理的区域。为了提高图像处理的效率和准确性,通常会在需要处理的区域内定义一个感兴趣的区域(ROI),并对该区域进行下一步的处理。ROI可以通过方框、圆、椭圆、不规则多边形等方式勾勒出需要处理的区域。在机器视觉软件中,常常通过图像处理算子和函数来计算ROI。比如,在OpenCV中可以使用cv::Rect、cv::RotatedRect、cv::Point等进行ROI的类型定义和计算;在Matlab中可以使用imrect、imellipse、impoly等函数实现ROI的定义和计算。

处理ROI的目的是为了便于在图像的区域中进行目标检测、物体跟踪、边缘检测、图像分割等操作。通过使用ROI,可以将不需要处理的区域从原始图像中排除掉,从而减少图像处理的复杂度和耗时,提高计算效率和准确性。

在这里插入图片描述

#设置ROI和掩码
poly_pts = numpy.array([[[0,368],[300,210],[340,210],[640,368]]])mask = np.zeros_like(gray_img)cv2.fillPoly(mask, pts, color)img_mask = cv2.bitwise_and(gray_img, mask)

霍夫变换

霍夫变换是一种常用的图像处理算法,用于在图像中检测几何形状(如直线、圆、椭圆等)。该算法最初是由保罗·霍夫于 1962 年提出的。简单来说,霍夫变换可以将在直角坐标系中表示的图形转换为极坐标系中的线或曲线,从而方便地进行形状的检测和识别。所以霍夫变换实际上一种由繁到简(类似降维)的操作。在应用中,霍夫变换的过程可以分为以下几个步骤:

  1. 针对待检测的形状,选择相应的霍夫曼变换方法。比如,如果要检测直线,可以使用标准霍夫变换;如果要检测圆形,可以使用圆霍夫变换。
  2. 将图像转换为灰度图像,并进行边缘检测,以获得待检测的形状目标的轮廓。
  3. 以一定的步长和角度范围在霍夫空间中进行投票,将所有可能的直线或曲线与它们可能在的极坐标空间中的位置相对应。
  4. 找到霍夫空间中的峰值,这些峰值表示形状的参数空间中存在原始图像中形状的可能性。
  5. 通过峰值位置在原始图像中绘制直线、圆等形状。

当使用 canny 进行边缘检测后图像可以交给霍夫变换进行简单图形(线、圆)等的识别。这里用霍夫变换在 canny 边缘检测结果中寻找直线。

# 示例代码,作者丹成学长:Q746876041
mask = np.zeros_like(edges)ignore_mask_color = 255 # 获取图片尺寸imshape = img.shape# 定义 mask 顶点vertices = np.array([[(0,imshape[0]),(450, 290), (490, 290), (imshape[1],imshape[0])]], dtype=np.int32)# 使用 fillpoly 来绘制 maskcv2.fillPoly(mask, vertices, ignore_mask_color)masked_edges = cv2.bitwise_and(edges, mask)# 定义Hough 变换的参数rho = 1 theta = np.pi/180threshold = 2min_line_length = 4 # 组成一条线的最小像素数max_line_gap = 5    # 可连接线段之间的最大像素间距# 创建一个用于绘制车道线的图片line_image = np.copy(img)*0 # 对于 canny 边缘检测结果应用 Hough 变换# 输出“线”是一个数组,其中包含检测到的线段的端点lines = cv2.HoughLinesP(masked_edges, rho, theta, threshold, np.array([]),min_line_length, max_line_gap)# 遍历“线”的数组来在 line_image 上绘制for line in lines:for x1,y1,x2,y2 in line:cv2.line(line_image,(x1,y1),(x2,y2),(255,0,0),10)color_edges = np.dstack((edges, edges, edges)) import math
import cv2
import numpy as np"""
Gray Scale
Gaussian Smoothing
Canny Edge Detection
Region Masking
Hough Transform
Draw Lines [Mark Lane Lines with different Color]
"""class SimpleLaneLineDetector(object):def __init__(self):passdef detect(self,img):# 图像灰度处理gray_img = self.grayscale(img)print(gray_img)#图像高斯平滑处理smoothed_img = self.gaussian_blur(img = gray_img, kernel_size = 5)#canny 边缘检测canny_img = self.canny(img = smoothed_img, low_threshold = 180, high_threshold = 240)#区域 Maskmasked_img = self.region_of_interest(img = canny_img, vertices = self.get_vertices(img))#霍夫变换houghed_lines = self.hough_lines(img = masked_img, rho = 1, theta = np.pi/180, threshold = 20, min_line_len = 20, max_line_gap = 180)# 绘制车道线output = self.weighted_img(img = houghed_lines, initial_img = img, alpha=0.8, beta=1., gamma=0.)return outputdef grayscale(self,img):return cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)def canny(self,img, low_threshold, high_threshold):return cv2.Canny(img, low_threshold, high_threshold)def gaussian_blur(self,img, kernel_size):return cv2.GaussianBlur(img, (kernel_size, kernel_size), 0)def region_of_interest(self,img, vertices):mask = np.zeros_like(img)   if len(img.shape) > 2:channel_count = img.shape[2]  ignore_mask_color = (255,) * channel_countelse:ignore_mask_color = 255cv2.fillPoly(mask, vertices, ignore_mask_color)masked_image = cv2.bitwise_and(img, mask)return masked_imagedef draw_lines(self,img, lines, color=[255, 0, 0], thickness=10):for line in lines:for x1,y1,x2,y2 in line:cv2.line(img, (x1, y1), (x2, y2), color, thickness)def slope_lines(self,image,lines):img = image.copy()poly_vertices = []order = [0,1,3,2]left_lines = [] right_lines = [] for line in lines:for x1,y1,x2,y2 in line:if x1 == x2:pass else:m = (y2 - y1) / (x2 - x1)c = y1 - m * x1if m < 0:left_lines.append((m,c))elif m >= 0:right_lines.append((m,c))left_line = np.mean(left_lines, axis=0)right_line = np.mean(right_lines, axis=0)for slope, intercept in [left_line, right_line]:rows, cols = image.shape[:2]y1= int(rows) y2= int(rows*0.6)x1=int((y1-intercept)/slope)x2=int((y2-intercept)/slope)poly_vertices.append((x1, y1))poly_vertices.append((x2, y2))self.draw_lines(img, np.array([[[x1,y1,x2,y2]]]))poly_vertices = [poly_vertices[i] for i in order]cv2.fillPoly(img, pts = np.array([poly_vertices],'int32'), color = (0,255,0))return cv2.addWeighted(image,0.7,img,0.4,0.)def hough_lines(self,img, rho, theta, threshold, min_line_len, max_line_gap):"""edge_img: 要检测的图片矩阵参数2: 距离r的精度,值越大,考虑越多的线参数3: 距离theta的精度,值越大,考虑越多的线参数4: 累加数阈值,值越小,考虑越多的线minLineLength: 最短长度阈值,短于这个长度的线会被排除maxLineGap:同一直线两点之间的最大距离"""lines = cv2.HoughLinesP(img, rho, theta, threshold, np.array([]), minLineLength=min_line_len, maxLineGap=max_line_gap)line_img = np.zeros((img.shape[0], img.shape[1], 3), dtype=np.uint8)line_img = self.slope_lines(line_img,lines)return line_imgdef weighted_img(self,img, initial_img, alpha=0.1, beta=1., gamma=0.):lines_edges = cv2.addWeighted(initial_img, alpha, img, beta, gamma)return lines_edgesdef get_vertices(self,image):rows, cols = image.shape[:2]bottom_left  = [cols*0.15, rows]top_left     = [cols*0.45, rows*0.6]bottom_right = [cols*0.95, rows]top_right    = [cols*0.55, rows*0.6] ver = np.array([[bottom_left, top_left, top_right, bottom_right]], dtype=np.int32)return ver

深度学习方法

  • 基于基于分割的方法
  • 基于检测的方法
  • 基于关键点的方法
  • 基于参数曲线的方法

LaneNet+H-Net车道线检测

论文链接:Towards End-to-End Lane Detection: an Instance Segmentation Approach
代码链接

LaneNet+H-Net的车道线检测方法,通过深度学习方法实现端到端的车道线检测,该方法包括两个主要组件,一个是实例分割网络,另一个是车道线检测网络。

该方法的主要贡献在于使用实例分割技术来区分不同车道线之间的重叠和交叉,并且应用多任务学习方法同时实现车道线检测和实例分割。具体来说,该方法将车道线检测问题转化为实例分割问题,使用 Mask R-CNN 实现车道线的分割和检测。通过融合两个任务的 loss 函数,同时对车道线检测和实例分割网络进行训练,实现端到端的车道线检测。

论文中的模型结构主要包括两部分:实例分割网络和车道线检测网络。实例分割网络采用 Mask R-CNN,由主干网络和 Mask R-CNN 网络两部分组成。车道线检测网络采用了 U-Net 结构,用于对掩码图像进行后处理,得到车道线检测结果。

  • LaneNet将车道线检测问题转为实例分割问题,即:每个车道线形成独立的实例,但都属于车道线这一类别;H-Net由卷积层和全连接层组成,利用转换矩阵H对同一车道线的像素点进行回归;
  • 对于一张输入图片,LaneNet负责输出实例分割结果,每条车道线一个标识ID,H-Net输出一个转换矩阵,对车道线像素点进行修正,并对修正后的结果拟合出一个三阶多项式作为预测的车道线;
    在这里插入图片描述
    在这里插入图片描述
    测试效果在这里插入图片描述

Ultra-Fast-Lane-Detection-V2

论文链接:Ultra Fast Deep Lane Detection with Hybrid Anchor Driven Ordinal Classification
代码:Ultra-Fast-Lane-Detection-V2
![

讲解模型部分的代码

  • backbone
  • layer
  • model_culane
  • model_tusimple
  • seg_model

backbone

backbone有两类主干方法,VGG和ResNet

  • class vgg16bn
  • class resnet
import torch,pdb
import torchvision
import torch.nn.modulesclass vgg16bn(torch.nn.Module):def __init__(self,pretrained = False):super(vgg16bn,self).__init__()model = list(torchvision.models.vgg16_bn(pretrained=pretrained).features.children())model = model[:33]+model[34:43]self.model = torch.nn.Sequential(*model)def forward(self,x):return self.model(x)
class resnet(torch.nn.Module):def __init__(self,layers,pretrained = False):super(resnet,self).__init__()#resnet有以下几种选择方式if layers == '18':model = torchvision.models.resnet18(pretrained=pretrained)elif layers == '34':model = torchvision.models.resnet34(pretrained=pretrained)elif layers == '50':model = torchvision.models.resnet50(pretrained=pretrained)elif layers == '101':model = torchvision.models.resnet101(pretrained=pretrained)elif layers == '152':model = torchvision.models.resnet152(pretrained=pretrained)elif layers == '50next':model = torchvision.models.resnext50_32x4d(pretrained=pretrained)elif layers == '101next':model = torchvision.models.resnext101_32x8d(pretrained=pretrained)elif layers == '50wide':model = torchvision.models.wide_resnet50_2(pretrained=pretrained)elif layers == '101wide':model = torchvision.models.wide_resnet101_2(pretrained=pretrained)elif layers == '34fca':model = torch.hub.load('cfzd/FcaNet', 'fca34' ,pretrained=True)else:raise NotImplementedErrorself.conv1 = model.conv1self.bn1 = model.bn1self.relu = model.reluself.maxpool = model.maxpoolself.layer1 = model.layer1self.layer2 = model.layer2self.layer3 = model.layer3self.layer4 = model.layer4def forward(self,x):x = self.conv1(x)x = self.bn1(x)x = self.relu(x)x = self.maxpool(x)x = self.layer1(x)x2 = self.layer2(x)x3 = self.layer3(x2)x4 = self.layer4(x3)return x2,x3,x4

layer

在这部分代码,是设置网络层的功能模块。其中有两个模块 : AddCoordinates和CoordConv,它们都是用于卷积操作的。这些模块是用于解决卷积神经网络中的"hole problem"(窟窿问题)的。

  • AddCoordinates用于叠加在输入上的坐标信息。具体来说,它将坐标x、y和r(若设置了with_r参数为True)与输入张量相连接。其中,x和y坐标在[-1, 1]范围内进行缩放,坐标原点在中心。r是距离中心的欧几里得距离,并缩放为[0,1]范围内。这样做的目的是为了给卷积层提供额外的位置信息,以便提高其对位置信息的感知。

  • CoordConv模块则利用AddCoordinates模块中加入的坐标信息进行卷积操作。在当前张量和坐标信息合并后,将结果输入到卷积层中进行卷积操作。其余参数与torch.nn.Conv2d相同。

需要注意的是,这些模块需要结合使用,AddCoordinates模块在CoordConv模块之前使用,以确保卷积层能够获取到足够的位置信息。

import torch
from torch import nnclass AddCoordinates(object):r"""Coordinate Adder Module as defined in 'An Intriguing Failing ofConvolutional Neural Networks and the CoordConv Solution'(https://arxiv.org/pdf/1807.03247.pdf).This module concatenates coordinate information (`x`, `y`, and `r`) withgiven input tensor.`x` and `y` coordinates are scaled to `[-1, 1]` range where origin is thecenter. `r` is the Euclidean distance from the center and is scaled to`[0, 1]`.Args:with_r (bool, optional): If `True`, adds radius (`r`) coordinateinformation to input image. Default: `False`Shape:- Input: `(N, C_{in}, H_{in}, W_{in})`- Output: `(N, (C_{in} + 2) or (C_{in} + 3), H_{in}, W_{in})`Examples:>>> coord_adder = AddCoordinates(True)>>> input = torch.randn(8, 3, 64, 64)>>> output = coord_adder(input)>>> coord_adder = AddCoordinates(True)>>> input = torch.randn(8, 3, 64, 64).cuda()>>> output = coord_adder(input)>>> device = torch.device("cuda:0")>>> coord_adder = AddCoordinates(True)>>> input = torch.randn(8, 3, 64, 64).to(device)>>> output = coord_adder(input)"""def __init__(self, with_r=False):self.with_r = with_rdef __call__(self, image):batch_size, _, image_height, image_width = image.size()y_coords = 2.0 * torch.arange(image_height).unsqueeze(1).expand(image_height, image_width) / (image_height - 1.0) - 1.0x_coords = 2.0 * torch.arange(image_width).unsqueeze(0).expand(image_height, image_width) / (image_width - 1.0) - 1.0coords = torch.stack((y_coords, x_coords), dim=0)if self.with_r:rs = ((y_coords ** 2) + (x_coords ** 2)) ** 0.5rs = rs / torch.max(rs)rs = torch.unsqueeze(rs, dim=0)coords = torch.cat((coords, rs), dim=0)coords = torch.unsqueeze(coords, dim=0).repeat(batch_size, 1, 1, 1)image = torch.cat((coords.to(image.device), image), dim=1)return imageclass CoordConv(nn.Module):r"""2D Convolution Module Using Extra Coordinate Information as definedin 'An Intriguing Failing of Convolutional Neural Networks and theCoordConv Solution' (https://arxiv.org/pdf/1807.03247.pdf).Args:Same as `torch.nn.Conv2d` with two additional argumentswith_r (bool, optional): If `True`, adds radius (`r`) coordinateinformation to input image. Default: `False`Shape:- Input: `(N, C_{in}, H_{in}, W_{in})`- Output: `(N, C_{out}, H_{out}, W_{out})`Examples:>>> coord_conv = CoordConv(3, 16, 3, with_r=True)>>> input = torch.randn(8, 3, 64, 64)>>> output = coord_conv(input)>>> coord_conv = CoordConv(3, 16, 3, with_r=True).cuda()>>> input = torch.randn(8, 3, 64, 64).cuda()>>> output = coord_conv(input)>>> device = torch.device("cuda:0")>>> coord_conv = CoordConv(3, 16, 3, with_r=True).to(device)>>> input = torch.randn(8, 3, 64, 64).to(device)>>> output = coord_conv(input)"""def __init__(self, in_channels, out_channels, kernel_size,stride=1, padding=0, dilation=1, groups=1, bias=True,with_r=False):super(CoordConv, self).__init__()in_channels += 2if with_r:in_channels += 1self.conv_layer = nn.Conv2d(in_channels, out_channels,kernel_size, stride=stride,padding=padding, dilation=dilation,groups=groups, bias=bias)self.coord_adder = AddCoordinates(with_r)def forward(self, x):x = self.coord_adder(x)x = self.conv_layer(x)return x

seg_model

  • 主要包含conv_bn_relu和SegHead
  • conv_bn_relu模块包含一个卷积层、一个BatchNorm层和ReLU激活函数。这些层的目的是将输入张量x进行卷积、BN和ReLU激活操作,并将结果返回。
  • SegHead模块实现了一个带有分支的分割头。它包括三个分支,它们分别对应于主干网络的不同层级。每个分支都由卷积、BN和ReLU激活函数组成,并使用双线性插值对它们的输出进行上采样。然后它们的输出会被拼接在一起,输入到一个包含一系列卷积层的组合中。最后,它输出一个张量,其中包含num_lanes + 1个通道,表示每个车道的掩码以及背景。
import torch
from utils.common import initialize_weightsclass conv_bn_relu(torch.nn.Module):def __init__(self,in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1,bias=False):super(conv_bn_relu,self).__init__()self.conv = torch.nn.Conv2d(in_channels,out_channels, kernel_size, stride = stride, padding = padding, dilation = dilation,bias = bias)self.bn = torch.nn.BatchNorm2d(out_channels)self.relu = torch.nn.ReLU()def forward(self,x):x = self.conv(x)x = self.bn(x)x = self.relu(x)return xclass SegHead(torch.nn.Module):def __init__(self,backbone, num_lanes):super(SegHead, self).__init__()self.aux_header2 = torch.nn.Sequential(conv_bn_relu(128, 128, kernel_size=3, stride=1, padding=1) if backbone in ['34','18'] else conv_bn_relu(512, 128, kernel_size=3, stride=1, padding=1),conv_bn_relu(128,128,3,padding=1),conv_bn_relu(128,128,3,padding=1),conv_bn_relu(128,128,3,padding=1),)self.aux_header3 = torch.nn.Sequential(conv_bn_relu(256, 128, kernel_size=3, stride=1, padding=1) if backbone in ['34','18'] else conv_bn_relu(1024, 128, kernel_size=3, stride=1, padding=1),conv_bn_relu(128,128,3,padding=1),conv_bn_relu(128,128,3,padding=1),)self.aux_header4 = torch.nn.Sequential(conv_bn_relu(512, 128, kernel_size=3, stride=1, padding=1) if backbone in ['34','18'] else conv_bn_relu(2048, 128, kernel_size=3, stride=1, padding=1),conv_bn_relu(128,128,3,padding=1),)self.aux_combine = torch.nn.Sequential(conv_bn_relu(384, 256, 3,padding=2,dilation=2),conv_bn_relu(256, 128, 3,padding=2,dilation=2),conv_bn_relu(128, 128, 3,padding=2,dilation=2),conv_bn_relu(128, 128, 3,padding=4,dilation=4),torch.nn.Conv2d(128, num_lanes+1, 1)# output : n, num_of_lanes+1, h, w)initialize_weights(self.aux_header2,self.aux_header3,self.aux_header4,self.aux_combine)# self.droput = torch.nn.Dropout(0.1)def forward(self,x2,x3,fea):x2 = self.aux_header2(x2)x3 = self.aux_header3(x3)x3 = torch.nn.functional.interpolate(x3,scale_factor = 2,mode='bilinear')x4 = self.aux_header4(fea)x4 = torch.nn.functional.interpolate(x4,scale_factor = 4,mode='bilinear')aux_seg = torch.cat([x2,x3,x4],dim=1)aux_seg = self.aux_combine(aux_seg)return aux_seg

未完待续!

相关文章:

车道线检测

前言 目前&#xff0c;车道线检测技术已经相当成熟&#xff0c;主要应用在自动驾驶、智能交通等领域。下面列举一些当下最流行的车道线检测方法&#xff1a; 基于图像处理的车道线检测方法。该方法是通过图像处理技术从摄像头传回的图像中提取车道线信息的一种方法&#xff0c…...

云渲染靠谱吗,使用云渲染会不会被盗作品?

云渲染靠谱吗、安全吗&#xff1f;如果使用 云渲染会不会被盗作品......Renderbus瑞云渲染作为一个正经的云渲染平台&#xff0c;也时不时会收到这类疑问&#xff0c;首先&#xff0c;瑞云渲染是肯定靠谱的,各位可以放心使用。另外小编也将在本篇教你如何辨别云渲染平台是否安全…...

什么是FPGA?关于FPGA基础知识 一起来了解FPGA lattice 深力科 MachXO3系列 LCMXO3LF-9400C-5BG256C

什么是FPGA&#xff1f;关于FPGA基础知识 一起来了解FPGA lattice 深力科 MachXO3系列 LCMXO3LF-9400C-5BG256C FPGA基础知识&#xff1a;FPGA是英文Field&#xff0d;Programmable Gate Array的缩写&#xff0c;即现场可编程门阵列&#xff0c;它是在PAL、GAL、CPLD等可编程器…...

有什么好用的云渲染?

在CG制作流程中&#xff0c;离线渲染一直是必要且耗时的环节。你的场景越复杂&#xff0c;渲染出现问题的可能性就越大&#xff0c;尤其是当你独自工作&#xff0c;没有人给你建议的时候&#xff0c;灯光、模型、场景任何一个环节渲染时出现问题都可能让你焦头烂额&#xff0c;…...

什么是医学影像PACS系统?PACS系统功能有哪些?作用有哪些?对接哪些设备?业务流程是什么?

一、什么是医学影像PACS系统 PACS&#xff1a;为Picture Archive and CommunicationSystem的缩写&#xff0c;是图象归档和通讯系统。PACS系统应用在医院影像科室的系统&#xff0c;主要的任务就是把日常产生的各种医学影像&#xff08;包括核磁&#xff0c;CT&#xff0c;超声…...

分布式缓存:什么是它以及为什么需要它?

前言 随着网络的快速发展&#xff0c;分布式应用变得越来越普遍。这种类型的应用程序需要访问多个组件和服务&#xff0c;而这些组件可能分散在不同的物理位置上。在这种情况下&#xff0c;由于网络通信的高延迟和低带宽&#xff0c;性能问题变得尤为明显。为解决这一问题&…...

MySQL基础(二十二)逻辑架构

1.逻辑架构剖析 1.1 第1层&#xff1a;连接层 系统&#xff08;客户端&#xff09;访问MySQL服务器前&#xff0c;做的第一件事就是建立TCP连接。 经过三次握手建立连接成功后&#xff0c;MySQL服务器对TCP传输过来的账号密码做身份认证、权限获取。 用户名或密码不对&#…...

《Kubernetes证书篇:使用TLS bootstrapping简化kubelet证书制作》

一、背景 Master apiserver启用TLS认证后&#xff0c;Node节点kubelet和kube-proxy要与kube-apiserver进行通信&#xff0c;必须使用CA签发的有效证书才可以&#xff0c;当Node节点很多时&#xff0c;这种客户端证书颁发需要大量工作&#xff0c;同样也会增加集群扩展复杂度。 …...

vue+elementui+nodejs机票航空飞机航班查询与推荐

语言 node.js 框架&#xff1a;Express 前端:Vue.js 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat 开发软件&#xff1a;VScode )本系统主要是为旅客提供更为便利的机票预定方式&#xff0c;同时提高民航的预定机票的工作效率。通过网络平台实现信息化和网络化&am…...

将ssh发布密钥添加到服务器的ssh授权密钥中,但是为什么我仍然无法ssh登录到此服务器?

我已经将ssh发布密钥添加到服务器的ssh授权密钥中&#xff0c;但是为什么我仍然无法ssh登录到此服务器&#xff1f; 即使将ssh公钥添加到服务器的授权密钥中&#xff0c;您也可能无法通过SSH登录到服务器&#xff0c;这有几个原因: 1.服务器的authorized_keys文件的权限不正确…...

LeetCode——子串能表示从 1 到 N 数字的二进制串

1016. 子串能表示从 1 到 N 数字的二进制串 - 力扣&#xff08;Leetcode&#xff09; 目录 一、题目 二、题目解读 三、代码 一、题目 给定一个二进制字符串 s 和一个正整数 n&#xff0c;如果对于 [1, n] 范围内的每个整数&#xff0c;其二进制表示都是 s 的 子字符串 &…...

看火山引擎DataLeap如何做好电商治理(二):案例分析与解决方案

接上篇&#xff0c;以短视频优质项目为例&#xff0c;火山引擎DataLeap平台治理团队会去对每天发布的这种挂购物车车短视频打上标签&#xff0c;识别这些短视频它是优质的还是低质的&#xff0c;以及具体原因。一个视频经过这个模型识别之后&#xff0c;会给到奖惩中心去做相应…...

MySQL笔记-多表查询

本文标签 : 多表查询 事务四大特性 并发事务问题 事务隔离级别 文章目录 目录 文章目录 一、多表查询 1.多表关系 2.多表查询概念 3.多表查询的分类 4.内连接 5.外连接 6.自连接 7.联合查询 8.子查询 1.标量子查询 2.列子查询 3.行子查询 4.表子查询 9.多表查询案例练习 二…...

如何用100天时间,让CSDN的粉丝数从0狂飙到10000

2022年10月7日&#xff0c;正式开通了CSDN账号。但因为工作忙的原因&#xff0c;一直没有时间写博客文章&#xff0c;也没有投入精力在CSDN上。理所当然的&#xff0c;我的粉丝数量很稳定&#xff0c;一直保持着0的记录。 2023年春节假期过后&#xff0c;有点空闲时间了&#x…...

各种同质图神经网络模型的理论和节点表征学习任务的集合包rgb_experiment

诸神缄默不语-个人CSDN博文目录 最近更新时间&#xff1a;2023.5.10 最早更新时间&#xff1a;2023.5.10 本文仅考虑同质图setting下的模型。 对于异质图场景&#xff0c;可以参考我写的另一篇博文&#xff1a;异质图神经网络&#xff08;持续更新ing…&#xff09; node2ve…...

【C++进阶之路】类和对象(中)

文章目录 前言六大默认成员函数 一.构造函数性质默认构造函数构造函数(需要传参) 二.析构函数性质默认析构函数练习 三.拷贝构造函数基本性质&#xff1a;形参必须是引用默认拷贝构造浅拷贝深拷贝自定义类型 四.赋值运算符重载函数基本特征全局的运算符重载函数局部的运算符重载…...

AIMD 为什么收敛(tcp reno/cubic 为什么好)

TCP 拥塞控制目标是缓解并解除网络拥塞&#xff0c;让所有流量公平共享带宽&#xff0c;合在一起就是公平收敛。 AIMD(几乎所有与拥塞控制相关的协议或算法都有 AIMD 的影子&#xff0c;包括 RoCE&#xff0c;BBRv2) 为什么收敛&#xff1f;我一般会给出下面的老图&#xff1a;…...

医院智能导诊系统,医院导航解决方案

随着现代医院规模不断扩大&#xff0c;功能区域越来越细化&#xff0c;面对复杂的楼宇结构&#xff0c;集中的就诊人流&#xff0c;患者在就诊中经常会面临找不到目的地的困境&#xff0c;就诊体验变差。针对这个问题&#xff0c;一些面积和规模都比较大的医院&#xff0c;已经…...

【论文复现】基于区块链的分布式光伏就地消纳交易模式研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

在滴滴和字节跳动划水4年,过于真实了...

先简单交代一下吧&#xff0c;沅哥是某不知名211的本硕&#xff0c;18年毕业加入滴滴&#xff0c;之后跳槽到了头条&#xff0c;一直从事测试开发相关的工作。之前没有实习经历&#xff0c;算是四年半的工作经验吧。 这四年半之间他完成了一次晋升&#xff0c;换了一家公司&am…...

tensorflow GPU训练环境布置

tensorflow GPU训练环境布置 一、显卡驱动安装1.1 如何处理**Failed to initialize NVML: Driver/library version mismatch的问题**1.2 卸载旧的版本1.3 驱动安装 1.3.1 利用apt 安装1.3.2 手动安装 二、安装CUDA2.1 确定CUDA版本2.2 下载文件1. 找匹配版本2. 选合适的平台 2…...

理解和使用Java中的枚举

枚举是一种特殊的数据类型&#xff0c;用于定义一组具名的常量。Java中的枚举类型可以包含多个枚举常量&#xff0c;每个常量都具有唯一的名称和值。本文将详细介绍Java中的枚举&#xff0c;包括为什么要使用枚举、枚举的好处、如何定义和使用枚举等。 为什么要使用枚举&#…...

C++和Java:哪种语言更适合你

C和Java&#xff1a;哪种语言更适合你 一、引言1 背景介绍2 问题阐述3 目的和意义 二、C与Java的介绍1 C的特点和优缺点2 Java的特点和优缺点3 两种语言的比较4 选择C的理由4.1 适合底层开发的特点4.2高效的编译器和运行速度4.3 自由且灵活的语言风格4.4 良好的内存管理能力 5 …...

FE_Vue学习笔记 框架的执行流程详解

1 分析脚手架结构 &#xff08;1&#xff09;CLI就是 command line interface 的缩写。Vue CLI官网&#xff1a;Vue CLI &#xff08;2&#xff09;安装过程&#xff1a; &#xff08;PS&#xff1a; 提前安装过node.js了&#xff0c;没有安装的可以打开这个&#xff1a;Downl…...

KingbaseES V8R6 等待事件之LWLock Buffer_IO

等待事件含义 当进程同时尝试访问相同页面时&#xff0c;等待其他进程完成其输入/输出(I/O)操作时&#xff0c;会发生LWLock:BufferIO等待事件。其目的是将同一页读取到共享缓冲区中。 每个共享缓冲区都有一个与LWLock:BufferIO等待事件相关联的I/O锁&#xff0c;每次都必须在共…...

桂院导航小程序 静态项目 二次开发教程

Gitee代码仓库&#xff1a;桂院导航小程序 先 假装 大伙都成功安装了静态项目&#xff0c;并能在 微信开发者工具 和 手机 上正确运行。 接着就是 将项目 改成自己的学校。 代码里的注释我就不说明了&#xff0c;有提到 我的学校 的文字都改成你自己的就行 1. 全局 app.json…...

即时通讯APP开发费用成本多少?

移动互联网的发展&#xff0c;为人们的通讯交流提供了非常多的便利&#xff0c;一些即时通讯APP的出现&#xff0c;将人与人的距离再一次缩短。通过即时通讯APP软件&#xff0c;人们可以随时随地了解身边发生的新鲜事物&#xff0c;以及和朋友探讨各类趣事&#xff0c;甚至可以…...

女生学大数据好找工作么

好不好找工作和性别无关&#xff0c;无论你是男生还是女生&#xff0c;找工作的时候首先要看的都是学历&#xff0c;然后是个人能力&#xff0c;其中还有一定的面试经验和简历加分项~ 不要自己先把这个性别限定死&#xff0c;你有能力都能找到工作&#xff0c;不满足企业要求都…...

02-mysql升级篇(rpm方式+压缩包升级)

文章目录 升级方式一、二进制方式安装1、下载mysql-5.7.42安装包&#xff08;mysql-5.7.37升级mysql-5.7.42&#xff09;2、备份数据库、my.cnf文件&#xff0c;停止mysql服务&#xff08;重要&#xff09;3、查看当前数据库版本3、上传 mysql-5.7.42-1.el7.x86_64.rpm-bundle.…...

【Java零基础入门篇】第 ④ 期 - 继承(三)

【Java零基础入门篇】第 ④ 期 - 继承&#xff08;三&#xff09; 博主&#xff1a;命运之光专栏&#xff1a;Java零基础入门 学习目标 1.掌握继承性的主要作用、实现、使用限制&#xff1b; 2.掌握this和super的含义及其用法&#xff1b; 3.掌握方法覆写的操作&#xff1b; 4.…...