You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

cv.warpPerspective透视变换后颜色丢失 车道线绘制不连续问题

车道线逆透视映射绘制不连续问题修复

问题背景

需求为将鸟瞰视角图像通过逆透视转换为正常透视视角图像,提取图中两条蓝色车道线后映射回原始鸟瞰图像,最终在原图上绘制连续的车道线。
涉及的两张示例图像:

  • 原始鸟瞰视角图像:birdview
  • 逆透视转换后的正常透视视角图像:perspektiv

原有实现代码如下:

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偏移,未做范围截断,会出现无效阈值,进一步导致颜色漏检。
  • 缺少坐标反向映射步骤:逆透视变换本质是对图像做坐标空间变换,正常视角图像和原始鸟瞰图像的像素坐标不存在一一对应关系,原有代码直接将正常视角下提取到的像素坐标套用到原图上绘制,点位错位必然导致线条断裂。
  • 提取的颜色掩码未做形态学优化:直接颜色阈值提取的掩码会存在小空洞、零散噪点,未做处理就取非零点会丢失部分车道像素。
  • 绘制方式鲁棒性差:逐点绘制单个像素未做平滑拟合,零散点位无法形成连续线条。

修复步骤

  1. 修正颜色提取逻辑,补充形态学处理
    统一颜色空间转换参数,对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
    
  2. 用逆透视矩阵做坐标映射
    生成逆透视变换矩阵时同步计算反向变换矩阵,将正常视角下提取到的车道点坐标映射回原始鸟瞰图的坐标空间,过滤越界点:
    # 生成正向逆透视矩阵时的参考点,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]
    
  3. 多项式拟合车道线,绘制连续平滑线条
    将提取到的车道点按左右车道分离,做二次多项式拟合,生成连续坐标后用多段线绘制粗线,替代原有的逐像素点绘制:
    # 按图像中线分离左右车道点
    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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 07:06:11