cv.warpPerspective透视变换后颜色丢失 车道线绘制不连续问题
车道线逆透视映射绘制不连续问题修复
问题背景
需求为将鸟瞰视角图像通过逆透视转换为正常透视视角图像,提取图中两条蓝色车道线后映射回原始鸟瞰图像,最终在原图上绘制连续的车道线。
涉及的两张示例图像:
- 原始鸟瞰视角图像:

- 逆透视转换后的正常透视视角图像:

原有实现代码如下:
lane_color = [255, 0, 0] #BGR-color def object_isolation(img, color): color = np.uint8([[color]]) hsv_color = cv.cvtColor(color, cv.COLOR_RGB2HSV) image_hsv = cv.cvtColor(img, cv.COLOR_RGB2HSV) lower_range = np.array([hsv_color[0][0][0]-30,hsv_color[0][0][1]-30,hsv_color[0][0][2]-30], dtype = np.int32) upper_range = np.array([hsv_color[0][0][0]+30,hsv_color[0][0][1]+30,hsv_color[0][0][2]+30], dtype = np.int32) mask = cv.inRange(image_hsv, lower_range, upper_range) result = cv.bitwise_and(img, img,mask = mask ) return result ...... image = object_isolation(normal_perspektiv, lane_color) # image only contains the Blue color lines = np.nonzero(image) nonzero_lane_y = lines[0] nonzero_lane_x = lines[1] for i in range(len(nonzero_lane_y)): frame[nonzero_lane_y[i]][nonzero_lane_x[i]] = [255, 0, 0] # frame is my original image
运行后绘制的蓝色车道线存在断裂、不连续问题。
问题根因
- 颜色空间转换逻辑错误:代码注释标注
lane_color为BGR格式,但颜色转换调用的是cv.COLOR_RGB2HSV参数,通道顺序不匹配会直接导致蓝色像素提取不全;同时OpenCV中HSV的H通道取值范围为0-179,原有代码直接对HSV值做±30偏移,未做范围截断,会出现无效阈值,进一步导致颜色漏检。 - 缺少坐标反向映射步骤:逆透视变换本质是对图像做坐标空间变换,正常视角图像和原始鸟瞰图像的像素坐标不存在一一对应关系,原有代码直接将正常视角下提取到的像素坐标套用到原图上绘制,点位错位必然导致线条断裂。
- 提取的颜色掩码未做形态学优化:直接颜色阈值提取的掩码会存在小空洞、零散噪点,未做处理就取非零点会丢失部分车道像素。
- 绘制方式鲁棒性差:逐点绘制单个像素未做平滑拟合,零散点位无法形成连续线条。
修复步骤
- 修正颜色提取逻辑,补充形态学处理
统一颜色空间转换参数,对HSV阈值做合法范围截断,添加形态学闭运算填充掩码空洞,连接断裂的车道区域:lane_color = [255, 0, 0] # BGR格式纯蓝 def object_isolation(img, color): color = np.uint8([[color]]) # 匹配cv.imread默认的BGR通道顺序,统一用BGR转HSV hsv_color = cv.cvtColor(color, cv.COLOR_BGR2HSV) image_hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV) h, s, v = hsv_color[0][0] # 阈值截断到合法范围,H通道上限为179,S、V通道范围0-255 lower_range = np.array([ max(0, h-15), max(0, s-50), max(0, v-50) ], dtype=np.uint8) upper_range = np.array([ min(179, h+15), min(255, s+50), min(255, v+50) ], dtype=np.uint8) mask = cv.inRange(image_hsv, lower_range, upper_range) # 3*3核做两次闭运算,填充掩码空洞 kernel = cv.getStructuringElement(cv.MORPH_RECT, (3,3)) mask = cv.morphologyEx(mask, cv.MORPH_CLOSE, kernel, iterations=2) result = cv.bitwise_and(img, img, mask=mask) return result, mask - 用逆透视矩阵做坐标映射
生成逆透视变换矩阵时同步计算反向变换矩阵,将正常视角下提取到的车道点坐标映射回原始鸟瞰图的坐标空间,过滤越界点:# 生成正向逆透视矩阵时的参考点,src为鸟瞰图上的点,dst为正常视角对应的点 # M = cv.getPerspectiveTransform(src_points, dst_points) # 计算反向变换矩阵,用于将正常视角坐标转回鸟瞰图坐标 M_inv = cv.getPerspectiveTransform(dst_points, src_points) _, ipm_mask = object_isolation(normal_perspektiv, lane_color) nonzero_y, nonzero_x = np.nonzero(ipm_mask) # 拼接齐次坐标用于矩阵变换 ipm_points = np.vstack((nonzero_x, nonzero_y, np.ones(len(nonzero_x)))).T # 坐标映射+归一化 bird_points = M_inv.dot(ipm_points.T).T bird_points = bird_points[:, :2] / bird_points[:, 2:] bird_points = bird_points.astype(np.int32) # 过滤超出图像边界的无效点 h, w = frame.shape[:2] valid_idx = (bird_points[:,0] >= 0) & (bird_points[:,0] < w) & (bird_points[:,1] >=0) & (bird_points[:,1] < h) valid_points = bird_points[valid_idx] - 多项式拟合车道线,绘制连续平滑线条
将提取到的车道点按左右车道分离,做二次多项式拟合,生成连续坐标后用多段线绘制粗线,替代原有的逐像素点绘制:# 按图像中线分离左右车道点 mid_x = w // 2 left_x = valid_points[valid_points[:,0] < mid_x, 0] left_y = valid_points[valid_points[:,0] < mid_x, 1] right_x = valid_points[valid_points[:,0] >= mid_x, 0] right_y = valid_points[valid_points[:,0] >= mid_x, 1] # 二次多项式拟合y到x的映射关系 left_fit = np.polyfit(left_y, left_x, 2) right_fit = np.polyfit(right_y, right_x, 2) # 生成全图高度范围的连续坐标 plot_y = np.linspace(0, h-1, h, dtype=np.int32) left_plot_x = (left_fit[0]*plot_y**2 + left_fit[1]*plot_y + left_fit[2]).astype(np.int32) right_plot_x = (right_fit[0]*plot_y**2 + right_fit[1]*plot_y + right_fit[2]).astype(np.int32) # 拼接点集,绘制3像素粗的连续车道线 left_pts = np.vstack((left_plot_x, plot_y)).T.reshape((-1,1,2)) right_pts = np.vstack((right_plot_x, plot_y)).T.reshape((-1,1,2)) cv.polylines(frame, [left_pts, right_pts], isClosed=False, color=[255,0,0], thickness=3)
内容的提问来源于stack exchange,提问作者Yiwei Miao
相关产品推荐
相关产品推荐

