Python----计算机视觉处理(Opencv:道路检测完整版:透视变换,提取车道线,车道线拟合,车道线显示,)
Python----计算机视觉处理(Opencv:道路检测之道路透视变换)
Python----计算机视觉处理(Opencv:道路检测之提取车道线)
Python----计算机视觉处理(Opencv:道路检测之车道线拟合)
Python----计算机视觉处理(Opencv:道路检测之车道线显示)
一、透视变换
def img_to_wrp(img):height, width,_= img.shape # 获取输入图像的高度和宽度# 定义源点(透视变换前的点)src = np.float32([[width // 2 - 75, height // 2], # 左侧车道线的源点[width // 2 + 100, height // 2], # 右侧车道线的源点[0, height], # 底部左侧点[width, height] # 底部右侧点])# 定义目标点(透视变换后的点)dst = np.float32([[0, 0], # 目标左上角[width, 0], # 目标右上角[0, height], # 目标左下角[width, height] # 目标右下角])# 计算透视变换矩阵M = cv2.getPerspectiveTransform(src, dst)# 计算逆透视变换矩阵W = cv2.getPerspectiveTransform(dst, src)# 应用透视变换img_wrp = cv2.warpPerspective(img, M, (width, height), flags=cv2.INTER_LINEAR)return img_wrp, W # 返回透视变换后的图像和逆变换矩阵
这段代码的作用是对输入图像进行透视变换,以便为后续的车道线检测做好准备。
首先,它获取输入图像的高度和宽度,并定义源点(`src`),这些点是透视变换前车道线和底边的具体位置。源点的选取基于图像中车道线的预期位置,以确保透视变换能够有效地将车道区域从图像的视角转变为一个鸟瞰视图。
接下来,定义目标点(`dst`),这些点表示在透视变换后的标准位置,即四个角分别为上左、上右、下左和下右,以便将车道区域展现为一个矩形。
然后,通过 `cv2.getPerspectiveTransform` 计算透视变换矩阵 `M`,同时也计算出逆变换矩阵 `W`。
最后,使用 `cv2.warpPerspective` 应用透视变换,将输入图像转换为新的视角,输出变换后的图像 `img_wrp` 和逆变换矩阵 `W`,这为后续的图像处理和车道线绘制提供了良好的基础。
二、提取车道线
def img_road_show1(img):img_Gaussian = cv2.GaussianBlur(img, (7, 7), sigmaX=1) # 对图像进行高斯模糊,减少噪声img_gray = cv2.cvtColor(img_Gaussian, cv2.COLOR_BGR2GRAY) # 将模糊后的图像转换为灰度图像img_Sobel = cv2.Sobel(img_gray, -1, dx=1, dy=0) # 使用Sobel算子进行边缘检测,提取水平边缘ret, img_threshold = cv2.threshold(img_Sobel, 127, 255, cv2.THRESH_BINARY) # 二值化处理kernel = np.ones((11, 11), np.uint8) # 创建一个11x11的结构元素img_dilate = cv2.dilate(img_threshold, kernel, iterations=2) # 膨胀操作,增强白色区域img_erode = cv2.erode(img_dilate, kernel, iterations=2) # 腐蚀操作,去除小噪声return img_erode # 返回处理后的图像def img_road_show2(img):# 将图像从BGR颜色空间转换为HLS颜色空间img_hls = cv2.cvtColor(img, cv2.COLOR_BGR2HLS)# 提取亮度通道l_channel = img_hls[:, :, 1]# 将亮度通道进行归一化处理l_channel = l_channel / np.max(l_channel) * 255# 创建与亮度通道同样大小的零数组binary_output1 = np.zeros_like(l_channel)# 根据亮度阈值提取车道线区域(亮度值范围为220到255)binary_output1[(l_channel > 220) & (l_channel < 255)] = 1# 提取黄色车道线img_lab = cv2.cvtColor(img, cv2.COLOR_BGR2Lab) # 将图像转换为Lab颜色空间# 将图像的下部分设置为黑色,以减少干扰img_lab[:, 240:, :] = (0, 0, 0)# 提取Lab颜色空间中的蓝色通道lab_b = img_lab[:, :, 2]# 如果蓝色通道的最大值大于100则归一化处理if np.max(lab_b) > 100:lab_b = lab_b / np.max(lab_b) * 255# 创建与亮度通道同样大小的零数组binary_output2 = np.zeros_like(l_channel)# 根据蓝色通道的阈值提取车道线区域(蓝色值范围为212到220)binary_output2[(lab_b > 212) & (lab_b < 220)] = 1# 创建结构元素,用于形态学操作kernel = np.ones((15, 15), np.uint8)# 对binary_output2进行膨胀操作,以增强车道线binary_output2 = cv2.dilate(binary_output2, kernel, iterations=1)# 对膨胀后的图像进行两次腐蚀操作,去除小噪声binary_output2 = cv2.erode(binary_output2, kernel, iterations=1)binary_output2 = cv2.erode(binary_output2, kernel, iterations=1)# 创建一个与binary_output1同样大小的零数组binary_output = np.zeros_like(binary_output1)# 将两个二值输出结合,提取最终车道线区域binary_output[(binary_output1 == 1) | (binary_output2 == 1)] = 1# 对最终的二值输出进行膨胀处理kernel = np.ones((15, 15), np.uint8)img_dilate_binary_output = cv2.dilate(binary_output, kernel, iterations=1)# 对膨胀后的图像进行腐蚀处理,以便进一步去噪声img_erode_binary_output = cv2.erode(img_dilate_binary_output, kernel, iterations=1)# 返回最终处理后的图像return img_erode_binary_output
这段代码包含两个函数 `img_road_show1` 和 `img_road_show2`,用于处理输入图像并提取车道线区域。
`img_road_show1` 首先对输入图像应用高斯模糊,以减少噪声,然后将其转换为灰度图并使用 Sobel 算子进行边缘检测,以提取水平边缘。接着,进行二值化处理,将边缘图像转为黑白图像。之后,通过形态学操作(膨胀和腐蚀)增强白色区域并去除噪声,最终返回处理后的图像。
`img_road_show2` 则使用 HLS 颜色空间提取亮度通道,并通过阈值划分提取亮度较高的车道线区域。接着,将图像转换为 Lab 颜色空间,提取蓝色通道并进行归一化,依次利用阈值检测提取特定颜色的车道线,然后通过形态学操作去噪。
最终,将两个二值化结果结合,提取出合并的车道线区域,进行再一次的膨胀和腐蚀操作,以确保得到干净的二值图像,最后返回这一处理后的图像,以供后续分析或显示。整体上,这两个函数通过不同的方式(边缘检测和颜色空间分析)强化了车道线的可见性,以便于自动驾驶或图像分析任务。
三、车道线拟合
def road_polyfit(img):# 获取图像的高度和宽度height, width = img.shape# 创建一个与输入图像相同大小的RGB图像,用于绘制窗口out_img = np.dstack((img, img, img))# 计算每一列的白色像素总和,得到直方图num_ax0 = np.sum(img, axis=0)# 找到直方图左侧和右侧的最高点位置,分别作为车道线的起始点img_left_argmax = np.argmax(num_ax0[:width // 2]) # 左侧最高点img_right_argmax = np.argmax(num_ax0[width // 2:]) + width // 2 # 右侧最高点# 获取图像中所有非零像素的x和y位置nonzeroy, nonzerox = np.array(img.nonzero())# 定义滑动窗口的数量windows_num = 10# 定义每个窗口的高度和宽度windows_height = height // windows_numwindows_width = 30# 定义在窗口中检测到的白色像素的最小数量min_pix = 400# 初始化当前窗口的位置,后续会根据检测结果更新left_current = img_left_argmaxright_current = img_right_argmaxleft_pre = left_current # 记录上一个左侧窗口位置right_pre = right_current # 记录上一个右侧窗口位置# 创建空列表以存储左侧和右侧车道线像素的索引left_lane_inds = []right_lane_inds = []# 遍历每个窗口进行车道线检测for window in range(windows_num):# 计算当前窗口的上边界y坐标win_y_high = height - windows_height * (window + 1)# 计算当前窗口的下边界y坐标win_y_low = height - windows_height * window# 计算左侧窗口的左右边界x坐标win_x_left_left = left_current - windows_widthwin_x_left_right = left_current + windows_width# 计算右侧窗口的左右边界x坐标win_x_right_left = right_current - windows_widthwin_x_right_right = right_current + windows_width# 在输出图像上绘制当前窗口cv2.rectangle(out_img, (win_x_left_left, win_y_high), (win_x_left_right, win_y_low), (0, 255, 0), 2)cv2.rectangle(out_img, (win_x_right_left, win_y_high), (win_x_right_right, win_y_low), (0, 255, 0), 2)# 找到在当前窗口中符合条件的白色像素索引good_left_index = ((nonzeroy >= win_y_high) & (nonzeroy < win_y_low)& (nonzerox >= win_x_left_left) & (nonzerox < win_x_left_right)).nonzero()[0]good_right_index = ((nonzeroy >= win_y_high) & (nonzeroy < win_y_low)& (nonzerox >= win_x_right_left) & (nonzerox < win_x_right_right)).nonzero()[0]# 将找到的索引添加到列表中left_lane_inds.append(good_left_index)right_lane_inds.append(good_right_index)# 如果找到的左侧像素数量超过阈值,更新左侧窗口位置if len(good_left_index) > min_pix:left_current = int(np.mean(nonzerox[good_left_index]))else:# 如果没有找到足够的左侧像素,则根据右侧像素的位置进行偏移if len(good_right_index) > min_pix:offset = int(np.mean(nonzerox[good_right_index])) - right_preleft_current = left_current + offset# 如果找到的右侧像素数量超过阈值,更新右侧窗口位置if len(good_right_index) > min_pix:right_current = int(np.mean(nonzerox[good_right_index]))else:# 如果没有找到足够的右侧像素,则根据左侧像素的位置进行偏移if len(good_left_index) > min_pix:offset = int(np.mean(nonzerox[good_left_index])) - left_preright_current = right_current + offset# 更新上一个窗口位置left_pre = left_currentright_pre = right_current# 将所有的索引连接成一个数组,以便后续提取像素点的坐标left_lane_inds = np.concatenate(left_lane_inds)right_lane_inds = np.concatenate(right_lane_inds)# 提取左侧和右侧车道线像素的位置leftx = nonzerox[left_lane_inds] # 左侧车道线的x坐标lefty = nonzeroy[left_lane_inds] # 左侧车道线的y坐标rightx = nonzerox[right_lane_inds] # 右侧车道线的x坐标righty = nonzeroy[right_lane_inds] # 右侧车道线的y坐标# 对左侧和右侧车道线进行多项式拟合,得到拟合曲线的参数left_fit = np.polyfit(lefty, leftx, 2) # 左侧车道线的二次多项式拟合right_fit = np.polyfit(righty, rightx, 2) # 右侧车道线的二次多项式拟合# 生成均匀分布的y坐标,用于绘制车道线ploty = np.linspace(0, height - 1, height)# 根据拟合的多项式计算左侧和右侧车道线的x坐标left_fitx = left_fit[0] * ploty ** 2 + left_fit[1] * ploty + left_fit[2]right_fitx = right_fit[0] * ploty ** 2 + right_fit[1] * ploty + right_fit[2]# 计算中间车道线的位置middle_fitx = (left_fitx + right_fitx) // 2# 在输出图像上标记车道线像素点out_img[lefty, leftx] = [255, 0, 0] # 左侧车道线out_img[righty,rightx]=[0,0,255]# 右侧车道线return left_fitx, right_fitx, middle_fitx, ploty
这段代码实现了车道线检测与拟合的功能,主要通过滑动窗口的方法来识别和拟合车道线。首先获取输入图像的高度和宽度,并创建一个与输入图像相同大小的RGB图像用于绘制结果。
接着,通过计算每一列的白色像素总和生成直方图,找到左右车道线的起始点。
然后,定义滑动窗口的数量、每个窗口的高度和宽度,设置检测到的白色像素的最小数量。接下来,遍历每个窗口,绘制窗口并在窗口内检测白色像素的索引。
如果在窗口中找到足够的白色像素,则更新当前窗口的位置;如果没有找到,则根据相邻车道线的像素位置进行调整。
最后,提取所有窗口中检测到的车道线像素的坐标,利用多项式拟合(使用二次多项式)计算出车道线的方程,并生成均匀分布的y坐标以绘制车道线。
最终,将左侧和右侧车道线的像素标记在输出图像上,并返回拟合的车道线坐标和对应的y坐标。这一过程有效地提取并可视化了车道线,便于后续的自动驾驶或图像分析应用。
四、车道线显示
def road_show_concatenate(img, img_wrp, W, img_road1, left_fitx, right_fitx, middle_fitx, ploty):# 创建一个与 img_road1 相同大小的零图像,数据类型为 uint8wrp_zero = np.zeros_like(img_road1).astype(np.uint8)# 创建一个三通道的零图像,用于存放车道线color_wrp = np.dstack((wrp_zero, wrp_zero, wrp_zero))# 组合车道线点的坐标,用于绘制# pts_left, pts_right, pts_middle 是车道线在(y, x)坐标系中的坐标点pts_left = np.transpose(np.vstack([left_fitx, ploty])) # 包括左侧车道线的坐标pts_right = np.transpose(np.vstack([right_fitx, ploty])) # 包括右侧车道线的坐标pts_middle = np.transpose(np.vstack([middle_fitx, ploty])) # 包括中间车道线的坐标# 在 zero 图像上绘制车道线cv2.polylines(color_wrp, np.int32([pts_left]), isClosed=False, color=(202, 124, 0), thickness=15) # 绘制左侧车道线cv2.polylines(color_wrp, np.int32([pts_right]), isClosed=False, color=(202, 124, 0), thickness=15) # 绘制右侧车道线cv2.polylines(color_wrp, np.int32([pts_middle]), isClosed=False, color=(202, 124, 0), thickness=15) # 绘制中间车道线# 将绘制的车道线通过逆透视变换映射到原始图像的空间newwarp = cv2.warpPerspective(color_wrp, W, (img.shape[1], img.shape[0]))# 将透视变换后的图像与原始图像进行加权融合result1 = cv2.addWeighted(img, 1, newwarp, 1, 0)# 创建一个背景图像,并将其初始化为中间灰色background_zero = np.zeros_like(img).astype(np.uint8) + 127# 将新变换的车道线图像与背景图像进行加权融合result = cv2.addWeighted(background_zero, 1, newwarp, 1, 0)# 将原始图像与透视变换的图像进行水平拼接concatenate_image1 = np.concatenate((img, img_wrp), axis=1)# 将加权融合的结果与车道线图像进行水平拼接concatenate_image2 = np.concatenate((result1, result), axis=1)# 将两个拼接结果进行垂直拼接,形成最终图像concatenate_image = np.concatenate((concatenate_image1, concatenate_image2), axis=0)return concatenate_image # 返回最终拼接的图像
这段代码的目的是将车道线可视化结果与输入图像进行拼接,以便于展示处理效果。
首先,通过创建一个与 `img_road1` 同样大小的零图像和一个三通道的零图像,准备绘制车道线。
然后,通过将左侧、右侧和中间车道线的坐标点(通过拟合获得)组合在一起以便绘制。
接着,使用 `cv2.polylines` 在零图像上绘制车道线,赋予其颜色和厚度。
接下来,利用逆透视变换将绘制的车道线图像映射到原始图像的空间,并将其与原始图像进行加权融合,生成一个包含车道线的图像。为提供背景对比,创建一个中间灰色的背景图像,并将车道线图像与该背景融合。
随后,将原始图像与透视变换后的图像以及加权融合的结果进行水平拼接。
最终,将这两个拼接结果进行垂直拼接,形成一个展示原始图像、透视图和加权结果的最终图像,便于查看车道线检测与处理效果。该过程为图像处理和计算机视觉任务提供了直观的可视化方式。
五、完整代码
import numpy as np
import cv2# 定义透视变换函数
def img_to_wrp(img):height, width,_= img.shape # 获取输入图像的高度和宽度# 定义源点(透视变换前的点)src = np.float32([[width // 2 - 75, height // 2], # 左侧车道线的源点[width // 2 + 100, height // 2], # 右侧车道线的源点[0, height], # 底部左侧点[width, height] # 底部右侧点])# 定义目标点(透视变换后的点)dst = np.float32([[0, 0], # 目标左上角[width, 0], # 目标右上角[0, height], # 目标左下角[width, height] # 目标右下角])# 计算透视变换矩阵M = cv2.getPerspectiveTransform(src, dst)# 计算逆透视变换矩阵W = cv2.getPerspectiveTransform(dst, src)# 应用透视变换img_wrp = cv2.warpPerspective(img, M, (width, height), flags=cv2.INTER_LINEAR)return img_wrp, W # 返回透视变换后的图像和逆变换矩阵# 定义图像处理函数,提取车道线
def img_road_show1(img):img_Gaussian = cv2.GaussianBlur(img, (7, 7), sigmaX=1) # 对图像进行高斯模糊,减少噪声img_gray = cv2.cvtColor(img_Gaussian, cv2.COLOR_BGR2GRAY) # 将模糊后的图像转换为灰度图像img_Sobel = cv2.Sobel(img_gray, -1, dx=1, dy=0) # 使用Sobel算子进行边缘检测,提取水平边缘ret, img_threshold = cv2.threshold(img_Sobel, 127, 255, cv2.THRESH_BINARY) # 二值化处理kernel = np.ones((11, 11), np.uint8) # 创建一个11x11的结构元素img_dilate = cv2.dilate(img_threshold, kernel, iterations=2) # 膨胀操作,增强白色区域img_erode = cv2.erode(img_dilate, kernel, iterations=2) # 腐蚀操作,去除小噪声return img_erode # 返回处理后的图像def img_road_show2(img):# 将图像从BGR颜色空间转换为HLS颜色空间img_hls = cv2.cvtColor(img, cv2.COLOR_BGR2HLS)# 提取亮度通道l_channel = img_hls[:, :, 1]# 将亮度通道进行归一化处理l_channel = l_channel / np.max(l_channel) * 255# 创建与亮度通道同样大小的零数组binary_output1 = np.zeros_like(l_channel)# 根据亮度阈值提取车道线区域(亮度值范围为220到255)binary_output1[(l_channel > 220) & (l_channel < 255)] = 1# 提取黄色车道线img_lab = cv2.cvtColor(img, cv2.COLOR_BGR2Lab) # 将图像转换为Lab颜色空间# 将图像的下部分设置为黑色,以减少干扰img_lab[:, 240:, :] = (0, 0, 0)# 提取Lab颜色空间中的蓝色通道lab_b = img_lab[:, :, 2]# 如果蓝色通道的最大值大于100则归一化处理if np.max(lab_b) > 100:lab_b = lab_b / np.max(lab_b) * 255# 创建与亮度通道同样大小的零数组binary_output2 = np.zeros_like(l_channel)# 根据蓝色通道的阈值提取车道线区域(蓝色值范围为212到220)binary_output2[(lab_b > 212) & (lab_b < 220)] = 1# 创建结构元素,用于形态学操作kernel = np.ones((15, 15), np.uint8)# 对binary_output2进行膨胀操作,以增强车道线binary_output2 = cv2.dilate(binary_output2, kernel, iterations=1)# 对膨胀后的图像进行两次腐蚀操作,去除小噪声binary_output2 = cv2.erode(binary_output2, kernel, iterations=1)binary_output2 = cv2.erode(binary_output2, kernel, iterations=1)# 创建一个与binary_output1同样大小的零数组binary_output = np.zeros_like(binary_output1)# 将两个二值输出结合,提取最终车道线区域binary_output[(binary_output1 == 1) | (binary_output2 == 1)] = 1# 对最终的二值输出进行膨胀处理kernel = np.ones((15, 15), np.uint8)img_dilate_binary_output = cv2.dilate(binary_output, kernel, iterations=1)# 对膨胀后的图像进行腐蚀处理,以便进一步去噪声img_erode_binary_output = cv2.erode(img_dilate_binary_output, kernel, iterations=1)# 返回最终处理后的图像return img_erode_binary_outputdef road_polyfit(img):# 获取图像的高度和宽度height, width = img.shape# 创建一个与输入图像相同大小的RGB图像,用于绘制窗口out_img = np.dstack((img, img, img))# 计算每一列的白色像素总和,得到直方图num_ax0 = np.sum(img, axis=0)# 找到直方图左侧和右侧的最高点位置,分别作为车道线的起始点img_left_argmax = np.argmax(num_ax0[:width // 2]) # 左侧最高点img_right_argmax = np.argmax(num_ax0[width // 2:]) + width // 2 # 右侧最高点# 获取图像中所有非零像素的x和y位置nonzeroy, nonzerox = np.array(img.nonzero())# 定义滑动窗口的数量windows_num = 10# 定义每个窗口的高度和宽度windows_height = height // windows_numwindows_width = 30# 定义在窗口中检测到的白色像素的最小数量min_pix = 400# 初始化当前窗口的位置,后续会根据检测结果更新left_current = img_left_argmaxright_current = img_right_argmaxleft_pre = left_current # 记录上一个左侧窗口位置right_pre = right_current # 记录上一个右侧窗口位置# 创建空列表以存储左侧和右侧车道线像素的索引left_lane_inds = []right_lane_inds = []# 遍历每个窗口进行车道线检测for window in range(windows_num):# 计算当前窗口的上边界y坐标win_y_high = height - windows_height * (window + 1)# 计算当前窗口的下边界y坐标win_y_low = height - windows_height * window# 计算左侧窗口的左右边界x坐标win_x_left_left = left_current - windows_widthwin_x_left_right = left_current + windows_width# 计算右侧窗口的左右边界x坐标win_x_right_left = right_current - windows_widthwin_x_right_right = right_current + windows_width# 在输出图像上绘制当前窗口cv2.rectangle(out_img, (win_x_left_left, win_y_high), (win_x_left_right, win_y_low), (0, 255, 0), 2)cv2.rectangle(out_img, (win_x_right_left, win_y_high), (win_x_right_right, win_y_low), (0, 255, 0), 2)# 找到在当前窗口中符合条件的白色像素索引good_left_index = ((nonzeroy >= win_y_high) & (nonzeroy < win_y_low)& (nonzerox >= win_x_left_left) & (nonzerox < win_x_left_right)).nonzero()[0]good_right_index = ((nonzeroy >= win_y_high) & (nonzeroy < win_y_low)& (nonzerox >= win_x_right_left) & (nonzerox < win_x_right_right)).nonzero()[0]# 将找到的索引添加到列表中left_lane_inds.append(good_left_index)right_lane_inds.append(good_right_index)# 如果找到的左侧像素数量超过阈值,更新左侧窗口位置if len(good_left_index) > min_pix:left_current = int(np.mean(nonzerox[good_left_index]))else:# 如果没有找到足够的左侧像素,则根据右侧像素的位置进行偏移if len(good_right_index) > min_pix:offset = int(np.mean(nonzerox[good_right_index])) - right_preleft_current = left_current + offset# 如果找到的右侧像素数量超过阈值,更新右侧窗口位置if len(good_right_index) > min_pix:right_current = int(np.mean(nonzerox[good_right_index]))else:# 如果没有找到足够的右侧像素,则根据左侧像素的位置进行偏移if len(good_left_index) > min_pix:offset = int(np.mean(nonzerox[good_left_index])) - left_preright_current = right_current + offset# 更新上一个窗口位置left_pre = left_currentright_pre = right_current# 将所有的索引连接成一个数组,以便后续提取像素点的坐标left_lane_inds = np.concatenate(left_lane_inds)right_lane_inds = np.concatenate(right_lane_inds)# 提取左侧和右侧车道线像素的位置leftx = nonzerox[left_lane_inds] # 左侧车道线的x坐标lefty = nonzeroy[left_lane_inds] # 左侧车道线的y坐标rightx = nonzerox[right_lane_inds] # 右侧车道线的x坐标righty = nonzeroy[right_lane_inds] # 右侧车道线的y坐标# 对左侧和右侧车道线进行多项式拟合,得到拟合曲线的参数left_fit = np.polyfit(lefty, leftx, 2) # 左侧车道线的二次多项式拟合right_fit = np.polyfit(righty, rightx, 2) # 右侧车道线的二次多项式拟合# 生成均匀分布的y坐标,用于绘制车道线ploty = np.linspace(0, height - 1, height)# 根据拟合的多项式计算左侧和右侧车道线的x坐标left_fitx = left_fit[0] * ploty ** 2 + left_fit[1] * ploty + left_fit[2]right_fitx = right_fit[0] * ploty ** 2 + right_fit[1] * ploty + right_fit[2]# 计算中间车道线的位置middle_fitx = (left_fitx + right_fitx) // 2# 在输出图像上标记车道线像素点out_img[lefty, leftx] = [255, 0, 0] # 左侧车道线out_img[righty,rightx]=[0,0,255]# 右侧车道线return left_fitx, right_fitx, middle_fitx, plotydef road_show_concatenate(img, img_wrp, W, img_road1, left_fitx, right_fitx, middle_fitx, ploty):# 创建一个与 img_road1 相同大小的零图像,数据类型为 uint8wrp_zero = np.zeros_like(img_road1).astype(np.uint8)# 创建一个三通道的零图像,用于存放车道线color_wrp = np.dstack((wrp_zero, wrp_zero, wrp_zero))# 组合车道线点的坐标,用于绘制# pts_left, pts_right, pts_middle 是车道线在(y, x)坐标系中的坐标点pts_left = np.transpose(np.vstack([left_fitx, ploty])) # 包括左侧车道线的坐标pts_right = np.transpose(np.vstack([right_fitx, ploty])) # 包括右侧车道线的坐标pts_middle = np.transpose(np.vstack([middle_fitx, ploty])) # 包括中间车道线的坐标# 在 zero 图像上绘制车道线cv2.polylines(color_wrp, np.int32([pts_left]), isClosed=False, color=(202, 124, 0), thickness=15) # 绘制左侧车道线cv2.polylines(color_wrp, np.int32([pts_right]), isClosed=False, color=(202, 124, 0), thickness=15) # 绘制右侧车道线cv2.polylines(color_wrp, np.int32([pts_middle]), isClosed=False, color=(202, 124, 0), thickness=15) # 绘制中间车道线# 将绘制的车道线通过逆透视变换映射到原始图像的空间newwarp = cv2.warpPerspective(color_wrp, W, (img.shape[1], img.shape[0]))# 将透视变换后的图像与原始图像进行加权融合result1 = cv2.addWeighted(img, 1, newwarp, 1, 0)# 创建一个背景图像,并将其初始化为中间灰色background_zero = np.zeros_like(img).astype(np.uint8) + 127# 将新变换的车道线图像与背景图像进行加权融合result = cv2.addWeighted(background_zero, 1, newwarp, 1, 0)# 将原始图像与透视变换的图像进行水平拼接concatenate_image1 = np.concatenate((img, img_wrp), axis=1)# 将加权融合的结果与车道线图像进行水平拼接concatenate_image2 = np.concatenate((result1, result), axis=1)# 将两个拼接结果进行垂直拼接,形成最终图像concatenate_image = np.concatenate((concatenate_image1, concatenate_image2), axis=0)return concatenate_image # 返回最终拼接的图像if __name__ == '__main__':# 读取图像文件 '15.png'img = cv2.imread('15.png')# 对图像进行透视变换,以便后续的车道线检测img_wrp, W = img_to_wrp(img)# 通过显示车道线的函数生成一个车道线显示图像img_road1 = img_road_show1(img_wrp)# 调用 road_polyfit 函数进行车道线的多项式拟合# left_fitx, right_fitx, middle_fitx 包含了左右车道线和中间车道线的 x 坐标# ploty 包含了 y 坐标的范围left_fitx, right_fitx, middle_fitx, ploty = road_polyfit(img_road1)# 将处理后的图像与绘制的车道线组合在一起concatenate_image = road_show_concatenate(img, img_wrp, W, img_road1, left_fitx, right_fitx, middle_fitx, ploty)# 显示最终的拼接图像cv2.imshow('concatenate_image', concatenate_image)# 等待用户按下任意键,以退出显示窗口cv2.waitKey(0)
相关文章:
Python----计算机视觉处理(Opencv:道路检测完整版:透视变换,提取车道线,车道线拟合,车道线显示,)
Python----计算机视觉处理(Opencv:道路检测之道路透视变换) Python----计算机视觉处理(Opencv:道路检测之提取车道线) Python----计算机视觉处理(Opencv:道路检测之车道线拟合) Python----计算机视觉处理࿰…...
javaweb自用笔记:Maven分模块设计与开发、Maven继承与聚合、Maven私服
Maven分模块设计与开发 Maven继承与聚合 继承 版本锁定 dependencies引入依赖,dependencyManagement不代表依赖被引入,如果要使用dependencyManagement下的依赖,还需要在dependencies里面定义 聚合 如果没有用聚合,将这个项目打…...
在PyCharm中出现 **全角字符与非英文符号混合输入** 的问题
在PyCharm中出现 全角字符与非英文符号混合输入 的问题(如 124345dfs$¥cvd)࿰…...
数字身份DID协议:如何用Solidity编写去中心化身份合约
本文提出基于以太坊的自主主权身份(SSI)实现方案,通过扩展ERC-734/ERC-735标准构建链上身份核心合约,支持可验证声明、多密钥轮换、属性隐私保护等特性。设计的三层架构体系将身份控制逻辑与数据存储分离,在测试网环境…...
Linux的RPM包管理详解
Linux的RPM包管理详解 引言 RPM(Red Hat Package Manager)是Linux系统中一种重要的软件包管理工具,它以“.rpm”为扩展名,广泛应用于基于Red Hat的Linux发行版,如CentOS、Fedora、openSUSE等。RPM包不仅简化了软件包…...
其它理论原则
ABC理论 假设(Assumption)影响行为(Behavior),行为最终影响结果(Consequence)。 如果产品经理认为同事是一个不讲道理的人,那么产品经理在和他交流时就会产生抵触的行为,…...
C++中的类和对象(上)
1 类的定义 1.1 类定义的格式 1 class为定义类的关键字,Stack为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省 略》。类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者成员函数…...
LLaMA-Factory 数据集成从入门到精通
一、框架概述 LLaMA-Factory 框架通过Alpaca/Sharegpt双格式体系实现多任务适配,其中Alpaca专注结构化指令微调(含SFT/DPO/预训练),Sharegpt支持多角色对话及多模态数据集成。核心配置依托 dataset_info.json 实现数据源映射、格…...
高级:JVM面试题深度剖析
一、引言 在Java技术面试中,JVM(Java虚拟机)相关知识是考察重点之一。深入理解JVM的内存模型、垃圾回收机制、类加载机制等,不仅能帮助开发者优化Java应用性能,还能在面试中展现深厚的技术功底。本文将针对这些高频知…...
Spring MVC 中 @ResponseBody 注解深度使用教程
一、注解核心作用 ResponseBody 是 Spring MVC 中处理 响应体内容 的核心注解,主要功能: 跳过视图解析器:直接返回数据而非视图名称自动数据转换:根据返回值类型自动转换响应格式(JSON/XML/纯文本)RESTfu…...
数据结构第一轮复习--第七章查找(包含课程代码)
基于数组实现顺序查找代码 //顺序查找的实现 typedef struct{ //查找表的数据结构(顺序表) ElemType *elem; //指向开辟的动态数组基址 (起始地址) int TableLen; //表的长度 }SSTable; //顺序查找 int Search_Seq(SSTable ST…...
Springboot JPA 集成ShardingSphere
Spring Boot集成JPA与ShardingSphere可通过以下步骤实现分库分表功能,需重点关注依赖配置、分片规则定义及JPA适配问题: 一、依赖配置 1. 引入核心依赖 在pom.xml中添加ShardingSphere和JPA相关依赖: <!-- ShardingSphere JDBC --&…...
详细介绍javaspringboot操控redis的高级特性1. 事务支持2. 发布/订阅3. Pipeline批量操作
Spring Boot 对 Redis 的操作提供了丰富的高级特性,以下是对事务支持、发布 / 订阅、Pipeline 批量操作的详细介绍: 事务支持 原理:Redis 事务是一个单独的隔离操作,它可以包含多个命令,这些命令要么全部执行&#x…...
第一次3D打印,一个简单的小方块(Rhino)
一、建模 打开犀牛,我们选择立方体 我们点击上册的中心点 输入0,然后回车0 而后我们输长度:10,回车确认 同样的,宽度10 高度同样是10 回车确认后,我们得到一个正方形 二、导出模型 我们选择文件—>保存…...
数据分享:汽车测评数据
说明:如需数据可以直接到文章最后关注获取。 1.数据背景 Car Evaluation汽车测评数据集是一个经典的机器学习数据集,最初由 Marko Bohanec 和 Blaz Zupan 创建,并在 1997 年发表于论文 "Classifier learning from examples: Common …...
硬盘分区格式之GPT(GUID Partition Table)笔记250406
硬盘分区格式之GPT(GUID Partition Table)笔记250406 GPT(GUID Partition Table)硬盘分区格式详解 GPT(GUID Partition Table)是替代传统 MBR 的现代分区方案,专为 UEFI(统一可扩展固…...
辉视智慧医院:以科技温度 重塑医疗未来新生态
大家是否想过,医院里的广播对讲系统也能变身‘智慧管家’?今天带您走进辉视智慧医院,看看他们如何用四大黑科技,让医患沟通更暖心、更高效! 一、物联网技术:医疗设备‘开口说话’,广播系统秒变‘…...
Google 发布 Sec-Gemini v1:用 AI 重塑网络安全防御格局?
在网络威胁日益复杂化、自动化程度不断提高的今天,防御方常常感到力不从心。为了扭转这一局面,Google 近日迈出了重要一步,宣布推出专为网络安全领域量身打造的实验性 AI 模型——Sec-Gemini v1。该模型由 Google 内部的 Sec-Gemini 团队成员…...
Android 使用ninja加速编译的方法
ninja的简介 随着Android版本的更迭,makefile体系逐渐增多,导致make单编模块的时间越来越长,每次都需要半个小时甚至更长时间,其原因为每次make都会重新加载所有mk文件,再生成ninja编译,此完整过程十分耗时…...
windterm终端软件使用
windterm终端软件使用 下载安装包:https://github.com/kingToolbox/WindTerm/releases ssh连接: 可以复用vscode连接ssh信息 onekey 相当于服务器主机的用户名和密码 点击配置标签如Linux,输入用户名如root, identity file指定本地公钥&a…...
《操作系统真象还原》第六章——完善内核
文章目录 [toc]前言调用约定和混合编程调用约定混合编程 实现打印函数print.S显卡的端口控制实现单个字符打印put_char定义数据类型put_char编码修改内核main.c验证put_char 实现字符串打印put_str 结语 前言 学完上一章后,我们已经完成了一个操作系统最基本的三个…...
上海餐饮市场数据分析与可视化
上海作为中国的经济中心和国际化大都市,其餐饮市场具有高度的多样性和竞争性。随着消费者需求的不断变化,餐饮行业的从业者和投资者需要深入了解市场现状和趋势,以便制定更有效的商业策略。本文将通过数据分析和可视化技术,深入探讨上海餐饮市场的现状和趋势,为餐饮从业者…...
不花钱也能玩GPT-4o,国内可用
家人们!最近GPT-4o生图功能真的离谱到不真实,各种吉卜力、宫崎骏、3D风格等刷爆小红书。 由于只有GPT官网,只有Plus用户才能用,很多小伙伴们都没有机会体验过GPT,本期就分享一个国内也能直接玩的方法。 第一步&#x…...
ResNet改进(22):提升特征选择能力的卷积神经网络SKNet
在计算机视觉领域,残差网络(ResNet)一直是图像分类任务中的经典架构。本文将介绍一种改进版的ResNet18,它融合了选择性核(SK)机制,能够自适应地调整不同感受野的特征权重,从而提升模型性能。下面我们将详细解析这个实现代码。 一、代码概述 这个Python脚本实现了一个带有S…...
实战代码:esp32-cam按钮控制手机拍照V1.0
#include <WiFi.h> #include <HTTPClient.h> // WiFi设置 const char* ssid “MIFI-020806-2.4G”; const char* password “12345678”; // 静态IP配置 IPAddress staticIP(192, 168, 1, 32); // 设置为固定IP IPAddress gateway(192, 168, 1, 1); // 网关地址…...
Base64是密码吗?编码与加密的本质区别
(本文完全由deepseek生成,特此声明!) 引言:一个让开发者“翻车”的经典误区 我们常看到类似这样的提问: “我用Base64加密了用户的密码,为什么还是被黑客破解了?” “Base64解…...
原理图输出网表及调入
一、输出网表操作步骤 (1)选中.dsn文件,选者N或进入tools下拉列表选择Creat Netlists (2)导出网表后的文件 二、网表的导入 (1)执行菜单命令“File-Import-Logic/netlist”,将原理…...
C++ 模板的应用——智能指针、STL库
#include "head.h" #include <stdio.h> using namespace std;void registerUser(vector<string>& number,vector<string>& passwd){string username;string Passwd;cout << "请输入账号:" << endl;cin >> use…...
基于层次建模与交叉注意力融合的医学视觉问答系统(HiCA-VQA)详解
论文地址:https://arxiv.org/pdf/2504.03135 一、论文结构概述 这篇论文提出了一种针对医学视觉问答(Medical Visual Question Answering, Med-VQA)的层次化建模框架 HiCA-VQA,旨在解决现有方法在层次化语义建模和跨模态融合上的不足。以下是论文的核心结构: 引言 介…...
比较与分析敏捷开发方法:XP、Scrum、FDD等的特点与适用场景
目录 前言1. 极限编程 (XP)1.1 极限编程的核心特点1.2 极限编程的适用场景 2. Scrum2.1 Scrum的核心特点2.2 Scrum的适用场景 3. 水晶方法 (Crystal)3.1 水晶方法的核心特点3.2 水晶方法的适用场景 4. 特征驱动开发 (FDD)4.1 特征驱动开发的核心特点4.2 特征驱动开发的适用场景…...
ICMP 协议深度解析
ICMP 协议深度解析 一、协议定位与核心作用 ICMP(互联网控制报文协议)是IP协议体系的"哨兵系统",专用于网络状态监控与异常反馈。其核心价值体现在: 轻量级控制:仅传递关键状态信息,不承载业务…...
C语言基础20
内容提要 预处理 库文件 预处理 C语言编译步骤 预处理 编译 汇编 链接 什么是预处理 预处理就是在源文件(.c文件)编译之前,所进行的一部分预备操作,这部分操作是由预处理程序自动完成。当源文件在编译时,编译…...
conda常用命令
要查看使用conda创建的虚拟环境,可以按照以下步骤操作: 打开终端或命令行工具:确保你已经打开了终端或命令行界面,以便输入conda命令。 输入命令查看环境列表: 使用以下任一命令查看conda创建的虚拟环境:…...
Ubunut18.04 离线安装MySQL 5.7.35
一、环境准备 1.1 官方下载MySQL5.7.35 完整包 1.2 上传包 & 解压 上传包名称是:mysql-server_5.7.35-1ubuntu18.04_amd64.deb-bundle.tar # 切换到上传目录 cd /home/MySQL # 解压: tar -xvf mysql-server_5.7.35-1ubuntu18.04_amd64.deb-bundle…...
地图与图层操作
地图文档本质上就是存储在磁盘上的地图,包括地理数据、图名、图例等一系列要素,当完成地图制作、图层要素标注及符号显示设置后,可以将其作为图层文件保存到磁盘中,在一个图层文件中,包括了定义如何在地图上描述地理数…...
红宝书第三十一讲:通俗易懂的包管理器指南:npm 与 Yarn
红宝书第三十一讲:通俗易懂的包管理器指南:npm 与 Yarn 资料取自《JavaScript高级程序设计(第5版)》。 查看总目录:红宝书学习大纲 一、基础概念 包管理器:帮你自动下载和管理第三方代码库(如…...
李建忠:智能体正将互联网从信息网络重构为行动网络
引言 模型正在从训练为主转换为推理为主的新范式,智能体正将互联网从信息网络重构为行动网络,我们正处在从人类使用互联网到 AI 代理使用互联网的转折点。这不是未来,而是此刻已经发生的颠覆。 3 月 22 日,在腾讯云架构师技术同…...
瑞芯微AI处理器详解
瑞芯微(Rockchip)的芯片产品线覆盖从低功耗MCU到高性能AIoT处理器,以下是其主流芯片系列及RK3568的市场定位分析: 一、瑞芯微主要芯片系列 旗舰级 RK3588:12nm工艺,4A764A55,6TOPS NPUÿ…...
Compose Multiplatform+Kotlin Multiplatfrom 第五弹跨平台
截图功能 Compose MultiplatformKotlin Multiplatfrom下实现桌面端的截图功能,起码搞了两星期,最后终于做出来了,操作都很流畅,截取的文件大小也正常,可参考支持讨论! 功能效果 代码实现 //在jvmMain下创…...
linux安装ollama
俩种方式都可 一、linux通过docker安装ollama镜像 1.下载安装ollama镜像 # 安装 Docker sudo yum install docker sudo systemctl start docker#docker查看所有容器 docker ps -a # 查看所有容器# docker查看指定容器 docker ps -a |grep ollama# 创建模型存储目录ÿ…...
113. 在 Vue 3 中使用 OpenLayers 实现鼠标移动显示坐标信息
✨ 写在前面 在地图类项目开发中,一个常见需求就是:实时获取用户鼠标在地图上的经纬度坐标,并展示在地图上。 本文将通过一个简单的案例,手把手带大家在 Vue 3 项目中集成 OpenLayers 地图库,并实现以下功能…...
跳跃游戏的最优解法——贪心算法的智慧与实践
跳跃游戏的最优解法——贪心算法的智慧与实践 跳跃游戏是一类经典的算法题,既有趣又充满挑战,不仅能锻炼思维能力,还能直观展现贪心算法的核心思想。今天,我们从题目入手,拆解贪心算法的原理,用通俗易懂的…...
搭建docker registry私服,并且支持https推送
搭建docker registry私服,并且支持https推送 一、为什么写这篇文章二、搭建过程三、验证 一、为什么写这篇文章 网上关于搭建docker registry的文章一大把,但是都是配置为http方式推送,且需要显示端口,这个在真正项目使用中&…...
UniApp Vue 3 中的网络请求封装及用法
在UniApp中,结合Vue 3的强大特性,进行网络请求的封装是项目中常见的需求。这样的封装不仅提高了代码的可维护性,还使得在组件中使用网络请求更加简洁。本文将详细介绍UniApp Vue 3中的网络请求封装,并提供一个简单的用法示例。 创…...
策略模式结合模板方法模式
之前学习了策略模式加模板方法模式 策略模式单独详解 模板方法模式单独详解 这里回忆起完全可以进行策略和模板方法模式的组合。 import java.util.HashMap; import java.util.Map;// 上下文对象(解决参数传递问题) class OrderContext {private final…...
每日算法-250407
记录一下今天刷的三道 LeetCode 题目。 2389. 和有限的最长子序列 题目 思路 排序 前缀和 二分查找 解题过程 理解题意: 题目要求我们对于 queries 数组中的每个查询值 q,找出 nums 数组中元素和 小于等于 q 的 最长子序列 的长度。注意,是子序列&am…...
【Git “ls-tree“ 命令详解】
本章目录: 1. 命令简介2. 命令的基本语法和用法基本语法常见使用场景示例 1:查看当前提交的文件树示例 2:查看某个分支的文件树示例 3:查看特定路径下的文件树 3. 命令的常用选项及参数常用选项: 4. 命令的执行示例示例 1…...
Text-to-SQL技术深度解析:从理论突破到工程实践
引言:Text-to-SQL的技术演进与当代价值 在当今数据驱动的商业环境中,结构化数据查询语言(SQL)仍然是访问和分析企业数据的核心工具。然而,SQL的专业性要求构成了数据民主化的主要障碍——据统计,仅约35%的开发人员接受过系统的SQL培训,而超过51%的专业岗位需要SQL技能。T…...
Spring Boot 整合 Servlet三大组件(Servlet / Filter / Listene)
Spring Boot 整合 “Servlet三大组件“ ( Servlet / Filter / Listene ) 目录如下: pom.xml配置 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.…...
react 18 可中断的理解以及应用
React 的“可中断(interruptible)”渲染,指的是 React 在执行渲染过程中可以暂停、中断、再继续或放弃更新。这是 React 18 引入的并发特性的一部分,目的是让界面响应更流畅,防止“卡顿”。 📖 举个例子&am…...