MATLAB车道线识别系统
function imgout = lanemarker(img)
global top bot
persistent tform
if isempty(tform)
movingPoints = [203 720; 585 460; 695 460 ; 1127 720];
fixedPoints = [320 720; 320 0; 960 0; 960 720];
tform = fitgeotrans(movingPoints,fixedPoints,'projective');
top = 220;
bot = 12;
end
topview = view2top(img,tform);
[flanel,flaner] = lanefinder(topview);
hpic = size(topview,1);
y = 1:hpic;
y = y';
xl = polyval(flanel,y);
xr = polyval(flaner,y);
dim = size(topview);
dim = [dim,3];
imref = imref2d(size(topview));
lanel = reshape([xl';y'],1,[]);
laner = [xr';y'];
laner = fliplr(laner);
laner = reshape(laner,1,[]);
xpic = zeros(dim,'uint8');
xpic = insertShape(xpic,'FilledPolygon',[lanel,laner],'Color',[0,255,0],'Opacity',0.3);
xpic = imwarp(xpic,tform.invert,'OutputView',imref);
img = imlincomb(1,img,1,xpic);
xpic = zeros(dim,'uint8');
xpic = insertShape(xpic,'Line',lanel,'LineWidth',20,'Color',[255,0,0],'Opacity',1);
xpic = insertShape(xpic,'Line',laner,'LineWidth',20,'Color',[255,0,0],'Opacity',1);
xpic = imwarp(xpic,tform.invert,'OutputView',imref);
imgout = imlincomb(1,img,1,xpic);
end
上面是主程序view2top函数是将图转换为俯视图的函数,就是由左图变成右边的图。view2top函数由很多函数来完成工作,下面做一些必要说明。
function topview = view2top(img,tform)
global top bot
th_sobelx = [35, 100];
th_sobely= [30, 255];
th_mag = [30, 255];
th_dir = [0.7, 1.3];
th_h = [10, 100];
th_s = [85, 255];
th_l = [0, 60];
gradient_comb = gradient_combine(img, th_sobelx, th_sobely, th_mag, th_dir);
hsl_comb = hsl_combine(img, th_h, th_s, th_l);
dim = size(img);
combined_result = zeros(dim(1:2));
combined_result = uint8(combined_result);
combined_result(top:end-bot,:,:) = comb_result(gradient_comb, hsl_comb);
% figure(1)
% imshow(combined_result)
imref = imref2d(size(combined_result));
topview = imwarp(combined_result,tform,'OutputView',imref);
% topview = topview(top:end-bot,:,:);
end
上面的函数实现了由道路图像变成车道线俯视图的过程,该函数首先将图片中的车辆线提出出来,gradient_combine和hsl_combine通过图像梯度和hsl色彩模式来提出可能的车道线comb_result来进一步优化结果。
梯度阈值提取的结果
function gradient_comb = gradient_combine(img, th_x, th_y, th_mag, th_dir)
global top bot
R = img(top:end-bot,:,:);
R = R(:,:,1);
dim = size(R);
sobelx = sobel_xy(R, 'x', th_x);
sobely = sobel_xy(R, 'y', th_y);
mag_img = mag_thresh(R, th_mag);
dir_img = dir_thresh(R, th_dir);
gradient_comb = zeros(dim,'uint8');
gradient_comb(((sobelx > 0) & (mag_img > 0) & (dir_img > 0)) ...
| ((sobelx > 0) & (sobely > 0))) = uint8(255);
end
上述函数实现了梯度阈值的过程,sobelx 给出水平阈值后的结果,sobely求出垂直梯度
阈值后的结果,mag_thresh给出梯度阈值后的结果,dir_img给出梯度方向阈值后的结果。下面给出了其中的各个结果。注意上面的阈值过程只作用于红色通道。
function hsl_comb = hsl_combine(img, th_h, th_s, th_l)
global top bot
hsl = img(top:end-bot,:,:);
hsl = rgb2hsl(hsl);
h = hsl(:,:,1);
s = hsl(:,:,2);
l = hsl(:,:,3);
dim = size(h);
h_th = ch_thresh(h, th_h);
s_th = ch_thresh(s, th_s);
l_th = ch_thresh(l, th_l);
hsl_comb = zeros(dim,'uint8');
hsl_comb(((s_th > 0) & (l_th == 0)) ...
| ((s_th == 0) & (h_th > 0) & (l_th >0))) = uint8(255);
end
上面一段函数实现了hsl色彩通道阈值提取的过程,其结果如下图所示。色彩空间中的HSL、HSV、HSB有什么区别?提到了什么是hsl色彩空间,matlab只提供了转hsv的方法没有提供rgb转hsl色彩空间的方法,该函数在下方已经提供。
hsl阈值提出的结果
function result = comb_result(grad, hsl)
dim = size(grad);
result = zeros(dim,'uint8');
result(grad>0) = uint8(100);
result(hsl>0) = uint8(255);
end
上面的函数将两种结果进行整合,得出下面的结果。最后将下图做透视变形就得出最初提到的俯视图。
得到俯视图就可以利用滑窗拟合得到左右车道线的两次多项式表示结果。
function [flanel,flaner] = lanefinder(topview)
persistent imgcounter avelanel avelaner flag
hpic = size(topview,1);
wpic = size(topview,2);
if isempty(imgcounter)
imgcounter = 0;
flag = 0;
avelanel = zeros(hpic,10);
avelaner = zeros(hpic,10);
end
hist = sum(topview);
midx = floor(wpic/2);
[~,leftx] = max(hist(1:midx));
[~,rightx] = max(hist(midx+1:end));
rightx = rightx+midx;
num = 9;
hwin = floor(hpic/num);
wwinhalf = 56;
minpix = 50;
[rowy,colx] = find(topview);
leftlane = [];
rightlane = [];
for i = num:-1:1
win_high = hwin*i;
win_low = hwin*(i-1);
vcount = rowy <= win_high & rowy > win_low;
lcount = vcount & (colx <= leftx+wwinhalf & colx > leftx-wwinhalf);
lpix = sum(lcount);
rcount = vcount & (colx <= rightx+wwinhalf & colx > rightx-wwinhalf);
rpix = sum(rcount);
if lpix > minpix
leftx = mean(colx(lcount));
leftx = round(leftx);
end
if rpix > minpix
rightx = mean(colx(rcount));
rightx = round(rightx);
end
leftlane = [leftlane; colx(lcount),rowy(lcount)];
rightlane = [rightlane; colx(rcount),rowy(rcount)];
end
flanel = polyfit(leftlane(:,2),leftlane(:,1),2);
flaner = polyfit(rightlane(:,2),rightlane(:,1),2);
t = 1:hpic;
t = t';
if flag == 0
imgcounter = imgcounter+1;
avelanel(:,imgcounter) = polyval(flanel,t);
avelaner(:,imgcounter) = polyval(flaner,t);
if imgcounter == 10
flag = 1;
end
else
avelanel = [avelanel(:,2:end),polyval(flanel,t)];
avelaner = [avelaner(:,2:end),polyval(flaner,t)];
flanel = polyfit(t,mean(avelanel,2),2);
flaner = polyfit(t,mean(avelaner,2),2);
end
% y = 1:hpic;
% xl = flanel(y);
% xr = flaner(y);
% imshow(topview)
% hold on
% plot(xl,y);
% plot(xr,y);
% hold off
end
上面的函数完成了二次多项式拟合的结果,其主要用于视频的处理,所以不仅仅利用一张图片来得到车道线的结果,而是利用了十帧图像的平均结果。下图是得出的左右车道线结果。
再将车辆线从俯视图反透视变形和原始图像叠加如下,最后给出一个视频结果。
function hsl = rgb2hsl(rgb)
dim = size(rgb);
drgb = reshape(double(rgb)/255,[],3);%change range to 0-1
mx = max(drgb,[],2);%max of the 3 colors
mn = min(drgb,[],2);%min of the 3 colors
L = (mx+mn)/2;%luminance is half of max value + min value
S = zeros(size(L));
% this set of matrix operations can probably be done as an addition...
zeroidx = (mx==mn);
S(zeroidx) = 0;
lowlidx=L <= 0.5;
calc = (mx-mn)./(mx+mn);
idx = lowlidx & (~ zeroidx);
S(idx) = calc(idx);
hilidx = L > 0.5;
calc = (mx-mn)./(2-(mx+mn));
idx = hilidx & (~ zeroidx);
S(idx) = calc(idx);
hsv = rgb2hsv(rgb);
hsv = reshape(hsv,[],3);
H = hsv(:,1);
hsl = reshape([H, S, L],dim);
hsl = uint8(hsl*255);
end
% Sobel算子在Matlab默认为 $\left\lbrack \begin{array}{ccc}1 & 2 & 1\\0 & 0 & 0\\-1
% & -2 & -1\end{array}\right\rbrack$,强调水平方向的边缘
function binary_output = sobel_xy(img, orient, thresh)
h = fspecial('sobel'); % 垂直方向sobel算子,强调水平边
dim = size(img);
timg = double(img);
if orient == 'x'
abs_sobel = abs(imfilter(timg,fliplr(h')));
else
abs_sobel = abs(imfilter(timg,h));
end
scale_factor = max(abs_sobel(:)) / 255;
timg = reshape(abs_sobel(:) / scale_factor,dim);
scaled_sobel = uint8(timg);
binary_output = zeros(dim,'uint8');
binary_output((scaled_sobel > thresh(1)) & (scaled_sobel <= thresh(2))) = uint8(255);
end
function binary_output = mag_thresh(img, thresh)
timg = double(img);
dim = size(img);
h = fspecial('sobel');
sobelx = imfilter(timg,fliplr(h'));
sobely = imfilter(timg,h);
gradmag = sqrt(sobelx.^2 + sobely.^2);
scale_factor = max(gradmag(:)) / 255;
scaled_mag = reshape(gradmag(:) / scale_factor,dim);
binary_output = zeros(dim,'uint8');
binary_output((scaled_mag > thresh(1)) & (scaled_mag <= thresh(2))) = uint8(255);
end
function kernel = sobel_kernel(dim)
half = floor(dim/2);
t = [half:1:dim-2, dim-1, dim-2:-1:half];
sobel = zeros(dim);
for i = 1:half
sobel(i,:) = t-i+1;
end
kernel = sobel-flipud(sobel);
end
function binary_output = dir_thresh(img, thresh)
timg = double(img);
dim = size(img);
h = sobel_kernel(15);
sobelx = imfilter(timg,h');
sobely = imfilter(timg,h);
absgraddir = atan(abs(sobely) ./ abs(sobelx));
binary_output = zeros(dim,'uint8');
binary_output((absgraddir > thresh(1)) & (absgraddir <= thresh(2))) = uint8(255);
end
function binary_output = ch_thresh(ch, thresh)
dim = size(ch);
binary_output = zeros(dim,'uint8');
binary_output((ch > thresh(1)) & (ch <= thresh(2))) = uint8(255);
end
function result = comb_result(grad, hsl)
dim = size(grad);
result = zeros(dim,'uint8');
result(grad>0) = uint8(100);
result(hsl>0) = uint8(255);
end