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检测
→ 左右线分类
→ 直线拟合
→ 显示结果

1284

被折叠的 条评论
为什么被折叠?



