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

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----计算机视觉处理&#xff08;Opencv:道路检测之道路透视变换) Python----计算机视觉处理&#xff08;Opencv:道路检测之提取车道线&#xff09; Python----计算机视觉处理&#xff08;Opencv:道路检测之车道线拟合&#xff09; Python----计算机视觉处理&#xff0…...

基于飞桨框架3.0本地DeepSeek-R1蒸馏版部署实战

深度学习框架与大模型技术的融合正推动人工智能应用的新一轮变革。百度飞桨&#xff08;PaddlePaddle&#xff09;作为国内首个自主研发、开源开放的深度学习平台&#xff0c;近期推出的3.0版本针对大模型时代的开发痛点进行了系统性革新。其核心创新包括“动静统一自动并行”&…...

docker初始环境搭建(docker、Docker Compose、portainer)

docker、Docker Compose和portainer的安装部署、使用 docker、Docker Compose和portainer的安装部署、使用一.安装docker1.失败的做法2.首先卸载旧版本&#xff08;没安装则下一步&#xff09;3.配置下载的yum来源&#xff0c;不然yum search搜不到4.安装启动docker5.替换国内源…...

开源RuoYi AI助手平台的未来趋势

近年来&#xff0c;人工智能技术的迅猛发展已经深刻地改变了我们的生活和工作方式。 无论是海外的GPT、Claude等国际知名AI助手&#xff0c;还是国内的DeepSeek、Kimi、Qwen等本土化解决方案&#xff0c;都为用户提供了前所未有的便利。然而&#xff0c;对于那些希望构建属于自…...

element-ui自制树形穿梭框

1、需求 由于业务特殊需求&#xff0c;想要element穿梭框功能&#xff0c;数据是二级树形结构&#xff0c;选中左边数据穿梭到右边后&#xff0c;左边数据不变。多次选中左边相同数据进行穿梭操作&#xff0c;右边数据会多次增加相同的数据。右边数据穿梭回左边时&#xff0c;…...

Linux系统学习Day04 阻塞特性,文件状态及文件夹查询

知识点4【文件的阻塞特性】 文件描述符 默认为 阻塞 的 比如&#xff1a;我们读取文件数据的时候&#xff0c;如果文件缓冲区没有数据&#xff0c;就需要等待数据的到来&#xff0c;这就是阻塞 当然写入的时候&#xff0c;如果发现缓冲区是满的&#xff0c;也需要等待刷新缓…...

Module模块化

导出&#xff1a;export关键字 export var color "red"; 重命名导出 在模块中使用as用导出名称表示本地名称。 import { add } from "./05-module-out.js"; 导入&#xff1a; import关键字 导入单个绑定 import { sum } from "./05-module-out.js&…...

Python基础——Pandas库

对象的创建 导入 Pandas 时&#xff0c;通常给其一个别名“pd”&#xff0c;即 import pandas as pd。作为标签库&#xff0c;Pandas 对象在 NumPy 数组基础上给予其行列标签。可以说&#xff0c;列表之于字典&#xff0c;就如 NumPy 之于 Pandas。Pandas 中&#xff0c;所有数…...

C++: 类型转换

C: 类型转换 &#xff08;一&#xff09;C语言中的类型转换volatile关键字 修饰const变量 &#xff08;二&#xff09;C四种强制类型转换1. static_cast2. reinterpret_cast3. const_cast4. dynamic_cast总结 (三)RTTI &#xff08;一&#xff09;C语言中的类型转换 在C语言中…...

[ctfshow web入门] 零基础版题解 目录(持续更新中)

ctfshow web入门 零基础版 前言 我在刷题之前没有学过php&#xff0c;但是会python和C&#xff0c;也就是说&#xff0c;如果你和我一样会一门高级语言&#xff0c;就可以开始刷题了。我会以完全没学过php的视角来写题解&#xff0c;你也完全没有必要专门学习php&#xff0c;这…...

【蓝桥杯】动态规划:线性动态规划

1. 最长上升子序列(LIS) 1.1. 题目 想象你有一排数字,比如:3, 1, 2, 1, 8, 5, 6 你要从中挑出一些数字,这些数字要满足两个条件: 你挑的数字的顺序要和原来序列中的顺序一致(不能打乱顺序) 你挑的数字要一个比一个大(严格递增) 问:最多能挑出多少个这样的数字? …...

STM32——DAC转换

DAC简介 DAC&#xff0c;全称&#xff1a;Digital-to-Analog Converter&#xff0c;扑指数字/模拟转换器 ADC和DAC是模拟电路与数字电路之间的桥梁 DAC的特性参数 1.分辨率&#xff1a; 表示模拟电压的最小增量&#xff0c;常用二进制位数表示&#xff0c;比如&#xff1a…...

Kafka的索引设计有什么亮点

想获取更多高质量的Java技术文章&#xff1f;欢迎访问Java技术小馆官网&#xff0c;持续更新优质内容&#xff0c;助力技术成长 Java技术小馆官网https://www.yuque.com/jtostring Kafka的索引设计有什么亮点&#xff1f; Kafka 之所以能在海量数据的传输和处理过程中保持高…...

在深度学习中,如何统计模型的 ​​FLOPs(浮点运算次数)​​ 和 ​​参数量(Params)

在深度学习中&#xff0c;统计模型的FLOPs&#xff08;浮点运算次数&#xff09;和参数量&#xff08;Params&#xff09;是评估模型复杂度和计算资源需求的重要步骤。 一、参数量&#xff08;Params&#xff09;计算 参数量指模型中所有可训练参数的总和&#xff0c;其计算与…...

智能手表该存什么音频和文本?场景化存储指南

文章目录 为什么需要“场景化存储”&#xff1f;智能手表的定位手机替代不了的场景碎片化的场景存储 音频篇&#xff1a;智能手表该存什么音乐和音频&#xff1f;运动场景通勤场景健康场景 文本篇&#xff1a;哪些文字信息值得放进手表&#xff1f;&#xff08;部分情况可使用图…...

Linux之Shell脚本--命令提示的写法

原文网址&#xff1a;Linux之Shell脚本--命令提示的写法-CSDN博客 简介 本文介绍Linux的Shell脚本命令提示的写法。 场景描述 在写脚本时经常会忘记怎么使用&#xff0c;需要进行命令提示。比如&#xff1a;输入-h参数&#xff0c;能打印用法。 实例 新建文件&#xff1a…...

Logo语言的进程

Logo语言的进程与发展 引言 Logo语言是一种专为儿童和教育目的而设计的编程语言&#xff0c;其独特之处在于其简洁的语法和直观的图形化界面&#xff0c;旨在帮助学生理解程序设计的基本概念。由于其在教育领域的广泛应用&#xff0c;Logo语言在编程教育史上占据了重要的地位…...

Day19 -实例:xcx逆向提取+微信开发者工具动态调试+bp动态抓包对小程序进行资产收集

思路&#xff1a; 拿到源码后的测试方向&#xff1a; Step1、xcx逆向提取源码 00x1 先将曾经使用小程序记录删除 00x2 访问小程序 例&#xff1a;汉川袁老四小程序 00x3 将文件给xcx进行逆向解包 xcx工具的目录下&#xff0c;wxpack文件夹内 Step2、微信开发者工具进行动态…...

鸿蒙Arkts开发飞机大战小游戏,包含无敌模式,自动射弹,暂停和继续

飞机大战可以把飞机改成图片&#xff0c;目前包含无敌模式&#xff0c;自动射弹&#xff0c;暂停和继续的功能 代码如下&#xff1a; // 定义位置类 class GamePosition {x: numbery: numberconstructor(x: number, y: number) {this.x xthis.y y} }Entry Component struct…...

ECharts配置优化

优化 ECharts 配置可以从性能优化、视觉优化和可维护性优化三个方面入手&#xff0c;下面我给你详细展开几个实用方向&#xff1a; ✅ 一、性能优化&#xff08;大数据量 or 页面卡顿时重点考虑&#xff09; 使用 setOption 的 notMerge 和 lazyUpdate chart.setOption(option,…...

从基础算力协作到超智融合,超算互联网助力大语言模型研习

一、背景 大语言模型&#xff08;LLMs&#xff09;的快速发展释放出了AI应用领域的巨大潜力。同时&#xff0c;大语言模型作为 AI领域的新兴且关键的技术进展&#xff0c;为 AI 带来了全新的发展方向和应用场景&#xff0c;给 AI 注入了新潜力&#xff0c;这体现在大语言模型独…...

M1使用docker制作镜像xxl-job,供自己使用

很苦逼一个情况,m1的docker假如不翻墙&#xff0c;我们找不到xxl-job,所以我们要自己制作 首先先去下载xxl-job源码https://gitee.com/xuxueli0323/xxl-job 你把它拉去到idea中 拉去成功后&#xff0c;进入这个xxl-job目录 执行 mvn clean package -Dmaven.test.skiptrue(这一步…...

Spring Boot 集成Redis 的Lua脚本详解

1. 对比Lua脚本方案与Redis自身事务 对比表格 对比维度Redis事务&#xff08;MULTI/EXEC&#xff09;Lua脚本方案原子性事务命令序列化执行&#xff0c;但中间可被其他命令打断&#xff0c;不保证原子性Lua脚本在Redis单线程中原子执行&#xff0c;不可中断计算能力仅支持Red…...

第一个简易SSM框架项目

引言 这是一个简易SSM整合项目&#xff0c;适合后端入门的练习项目&#xff0c;其中没有太多的业务操作&#xff0c;主要是这个框架&#xff0c;以及编码的顺序&#xff0c;希望大家有所收获 首先需要先配置环境 数据库环境 创建一个存放书籍的数据库表 create database s…...

Node.js局部生效的中间件

目录 1. 目录结构 2. 代码实现 2.1 安装Express 2.2 app.js - 主文件 2.3 authMiddleware.js - 局部生效的中间件 3. 程序运行结果 4. 总结 在Node.js的Express框架中&#xff0c;局部生效的中间件是指仅在特定路由或路由组中生效的中间件。它可以用于权限验证、数据过滤…...

Nginx 常见面试题

一、nginx常见错误及处理方法 1.1 404 bad request 一般原因&#xff1a;请求的Header过大 解决办法&#xff1a; 配置nginx.conf 相关设置1. client_header_buffer_size 16k; 2. large_client_header_buffers 4 64k;1.2 413 Request Entity Too Large 一般原因&#xff1…...

golang 计时器内存泄露问题 与 pprof 性能分析工具

&#xff08;上图用 go tool pprof 工具分析生成&#xff09; 这种会造成内存泄露 因为每次for都会新建一个time对象&#xff0c;只有到期后会被回收。 解决方法&#xff1a;用time.NewTimer与time.Reset每次重新激活定时器 背景 我先贴一下会发生内存泄漏的代码段&#xff0c…...

C#调用C++动态库时出现`System.DllNotFoundException`错误的解决思路

文章目录 1. DLL文件路径问题2. 依赖的运行时库缺失3. 平台不匹配&#xff08;x86/x64&#xff09;4. 导出函数名称不匹配5. DLL文件损坏或权限问题6. 运行时库冲突&#xff08;MT/MD不匹配&#xff09;7. 使用DLLImport时的常见错误总结步骤 在C#中调用C动态库时出现System.Dl…...

深度学习的下一个突破:从图像识别到情境理解

引言 过去十年&#xff0c;深度学习在图像识别领域取得了惊人的突破。从2012年ImageNet大赛上的AlexNet&#xff0c;到后来的ResNet、EfficientNet&#xff0c;再到近年来Transformer架构的崛起&#xff0c;AI已经能在许多任务上超越人类&#xff0c;比如人脸识别、目标检测、医…...

oracle查询是否锁表了

--查看当前数据库中被锁定的表数量 SELECT COUNT(*) FROM v$locked_object; select * from v$locked_object; --查看具体被锁定的表 SELECT b.owner, b.object_name, a.session_id, a.locked_mode FROM v$locked_object a, dba_objects b WHERE b.object_id a.object_id…...