计算机控制系统-车道线提取的代码

1 步骤

步骤1:图像采集与灰度化

读取前视相机图像后,首先把彩色图像转为灰度图。灰度化不是为了“丢掉信息”,而是为了突出亮度变化,降低 RGB 三通道直接处理带来的复杂度。MATLAB 中对应函数是 rgb2gray。

步骤2:滤波与边缘检测

车道线通常表现为明显的亮度跳变,所以边缘检测是非常有效的起点。为了减少噪声,建议先做高斯滤波,再用 Canny 算法提取边缘。Canny 的两个阈值决定“多强的梯度才算边缘”,阈值过低会把纹理和阴影也识别成边缘,阈值过高又会漏检车道线。

步骤3:设置 ROI 感兴趣区域

不是整幅图都要分析。真正与车辆控制最相关的区域是“车辆前方路面”这块区域,因此通常保留图像下部、舍弃天空、远处建筑和车身上部。对直道路面,最常用的 ROI 是下宽上窄的梯形。

步骤4:Hough 变换检测直线

边缘图中仍然会有很多碎边,需要借助 Hough 变换把“属于同一条直线的边缘点”聚合起来。Hough 空间中的峰值对应图像中的候选直线。hough、houghpeaks 和 houghlines 三个函数通常配套使用。

步骤5:左右车道分组与拟合

Hough 输出的是很多短线段,不是最终要用的左、右车道线。还需要根据线段的位置、斜率或角度,把它们分成左线与右线两组,再利用 polyfit 做一次直线拟合,得到更稳定的左右边界。

步骤6:结果可视化与故障排查

把 ROI、候选线段、最终拟合线同时显示出来,便于学生判断错误是出在“边缘太多”“ROI 太大”“Hough 参数不合适”还是“左右分组条件不合理”。

2 代码

2.1 函数

function [draw_y, draw_lx, draw_rx, state, BW] = detectLaneFromFrame(pic, lastL, lastR)
% 输入:
%   pic   : 当前帧彩色图像
%   lastL : 上一帧左车道线 x 坐标
%   lastR : 上一帧右车道线 x 坐标
%
% 输出:
%   draw_y  : 绘制车道线的 y 坐标
%   draw_lx : 左车道线对应 x 坐标
%   draw_rx : 右车道线对应 x 坐标
%   state   : 检测状态
%             0 正常
%             1 仅检测到左线
%             2 仅检测到右线
%             3 左右都未检测到
%   BW      : ROI 内的二值边缘图

    draw_lx = [];
    draw_rx = [];
    state = 0;

    %% 1. 灰度化
    gray_pic = rgb2gray(pic);

    %% 2. 高斯滤波
    sigma = 2;
    window = 2 * ceil(3*sigma) + 1;
    H = fspecial('gaussian', [window window], sigma);
    gray_pic = imfilter(gray_pic, H, 'replicate');

    %% 3. 边缘检测
    edge_pic = edge(gray_pic, 'canny', [0.05 0.20]);

    %% 4. ROI 区域设置
    [h, w] = size(edge_pic);

    % 建议使用六边形/梯形 ROI,而不是简单矩形
    roi_x = [w*0.20, w*0.45, w*0.55, w*0.95, w*0.95, w*0.05];
    roi_y = [h*0.95, h*0.60, h*0.60, h*0.95, h*1.00, h*1.00];

    bw = roipoly(edge_pic, roi_x, roi_y);
    BW = edge_pic & bw;

    %% 5. Hough 变换
    [HoughM, T, R] = hough(BW, 'RhoResolution', 1, 'ThetaResolution', 1);

    % 峰值数量可根据视频复杂度调整
    P = houghpeaks(HoughM, 8, 'Threshold', ceil(0.25 * max(HoughM(:))));

    lines = houghlines(BW, T, R, P, 'FillGap', 20, 'MinLength', 25);

    %% 6. 按斜率分左右线
    leftlines = [];
    rightlines = [];

    for k = 1:length(lines)
        x1 = lines(k).point1(1); y1 = lines(k).point1(2);
        x2 = lines(k).point2(1); y2 = lines(k).point2(2);

        dx = x2 - x1;
        dy = y2 - y1;

        % 避免除零
        if abs(dx) < 1e-6
            continue;
        end

        slope = dy / dx;

        % 过滤水平线和过于平缓的线
        if abs(slope) < 0.3
            continue;
        end

        % 图像左半部分负斜率通常为左车道
        if slope < 0 && x1 < w*0.7 && x2 < w*0.7
            leftlines = [leftlines; x1 y1; x2 y2];
        end

        % 图像右半部分正斜率通常为右车道
        if slope > 0 && x1 > w*0.3 && x2 > w*0.3
            rightlines = [rightlines; x1 y1; x2 y2];
        end
    end

    %% 7. 拟合左右车道线
    draw_y = [h*0.62, h*0.95];

    if ~isempty(leftlines)
        PL = polyfit(leftlines(:,2), leftlines(:,1), 1);
        draw_lx = polyval(PL, draw_y);
    end

    if ~isempty(rightlines)
        PR = polyfit(rightlines(:,2), rightlines(:,1), 1);
        draw_rx = polyval(PR, draw_y);
    end

    %% 8. 检测失败补偿
    if isempty(draw_lx) && ~isempty(lastL)
        draw_lx = lastL;
    end

    if isempty(draw_rx) && ~isempty(lastR)
        draw_rx = lastR;
    end

    %% 9. 状态标记
    if ~isempty(draw_lx) && ~isempty(draw_rx)
        state = 0;
    elseif ~isempty(draw_lx) && isempty(draw_rx)
        state = 1;
    elseif isempty(draw_lx) && ~isempty(draw_rx)
        state = 2;
    else
        state = 3;
    end
end

2.2 主代码(需要指定视频文件)

clc; clear; close all;

%% 1. 读取视频
videoFile = 'LaneVideo.mp4';   % 改成你自己的视频文件名
vidObj = VideoReader(videoFile);

% 是否保存结果视频
saveVideo = true;
if saveVideo
    outObj = VideoWriter('LaneVideo_Result.mp4', 'MPEG-4');
    outObj.FrameRate = vidObj.FrameRate;
    open(outObj);
end

%% 2. 初始化上一帧车道线,用于检测失败时补偿
lastL = [];
lastR = [];

frameIdx = 0;

%% 3. 逐帧处理
while hasFrame(vidObj)
    frameIdx = frameIdx + 1;
    pic = readFrame(vidObj);

    % 调用车道检测函数
    [draw_y, draw_lx, draw_rx, state, BW] = detectLaneFromFrame(pic, lastL, lastR);

    % 若本帧检测成功,则更新上一帧结果
    if ~isempty(draw_lx) && ~isempty(draw_rx)
        lastL = draw_lx;
        lastR = draw_rx;
    end

    %% 4. 显示结果
    resultImg = pic;
    figure(1); clf;
    imshow(resultImg); hold on;

    % 显示ROI边界
    [h, w, ~] = size(pic);
    a = [w*0.20, w*0.45, w*0.55, w*0.95, w*0.95, w*0.05];
    b = [h*0.95, h*0.60, h*0.60, h*0.95, h*1.00, h*1.00];
    plot([a a(1)], [b b(1)], 'y-', 'LineWidth', 2);

    % 画左右车道线
    if ~isempty(draw_lx)
        plot(draw_lx, draw_y, 'r-', 'LineWidth', 3);
    end
    if ~isempty(draw_rx)
        plot(draw_rx, draw_y, 'r-', 'LineWidth', 3);
    end

    % 画中心线
    if ~isempty(draw_lx) && ~isempty(draw_rx)
        cen_x = (draw_lx + draw_rx) / 2;
        plot(cen_x, draw_y, 'b--', 'LineWidth', 2);
    end

    title(sprintf('Frame = %d, State = %d', frameIdx, state));
    hold off;
    drawnow;

    %% 5. 保存结果视频
    if saveVideo
        frameOut = getframe(gcf);
        writeVideo(outObj, frameOut);
    end
end

%% 6. 关闭视频
if saveVideo
    close(outObj);
end

disp('视频处理完成。');

3 相关说明

如果你想先不保存视频,只想实时看效果:saveVideo = false;

视频里最常见的问题是:

  • 远处道路太窄,边缘多
  • 天空、树木、建筑物干扰
  • 车辆阴影或路面裂缝被误识别成车道线

所以 ROI 不建议用你最开始那种简单矩形:

a=[shape(2)*0.2 shape(2)*1 shape(2)*1 shape(2)*0.2];
b=[shape(1)*0.55 shape(1)*0.5 shape(1) shape(1)];

更推荐用梯形/六边形,因为道路本身是透视收缩的。

edge(gray_pic, 'canny', [0.05 0.20])

视频读取
 → 逐帧取图
 → 灰度化
 → 边缘检测
 → ROI提取
 → Hough检测
 → 左右线分类
 → 直线拟合
 → 显示结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

做一个码农都是奢望

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值