主要内容

评估和可视化Lane Boundary detection Against Ground Truth

这个例子展示了如何评估车道边界检测的性能与已知的地面真值。在本例中,您将通过计算拟合度度量来描述每帧车道边界检测算法的性能。此度量可用于查明、可视化和理解底层算法中的故障模式。

概述

随着人们对基于视觉的自动驾驶问题解决方案越来越感兴趣,能够评估和验证检测算法的准确金宝搏官方网站性变得非常重要。对于具有多个参数的检测算法,验证准确性尤其重要,这些参数可以通过调整来实现满足预定义质量要求的结果。本例将介绍一个这样的工作流,其中可以测量车道边界的精度水平。这个工作流有助于在每帧的基础上确定这些算法中的故障模式,以及描述其总体性能。这个工作流还可以帮助您直观地和定量地理解算法的性能。然后,您可以使用这种理解来优化底层算法,以提高其性能。

负载地面真实数据

本例中使用的数据集是一辆行驶在街道上的车辆上的前置摄像头的视频文件。车道边界的地面真相已经用地面真相标签应用程序在视频上手动标记,使用线ROI标记为“lane boundary”。这个视频是8秒,或250帧长。它有三个十字路口,几辆车(停着和移动着),和车道边界(双线,单线和虚线)。为您自己的视频创建一个地面真线边界数据集,您可以使用地面实况贴标签机应用程序。

%加载MAT文件与地面真实数据。加载=加载(“加州理工学院科尔多瓦1号”和“汽车地面真相.mat”);

加载Structure包含三个字段:

  1. groundTruthData,有两栏的时间表:LaneBoundaries车辆LaneBoundaries包含自我通道边界的地面真值点(左和右),表示为XY点的单元阵列,形成一条多边形线。车辆包含相机视图中车辆的地面真实边界框,表示为m × 4数组[x,y,宽度,高度]。

  2. 传感器,一个monoCamera物体带有关于安装在车辆上的校准摄像机的属性。这个物体可以让你估计真实世界中车辆和前面物体之间的距离。

  3. 视频名称,一个字符数组,包含存储帧的视频文件名。

从该结构中的数据中,使用VideoReader在帧之间循环。帧VideoReader对象使用helperMonoSensor对象使用存储在中的摄影机设置检测视频帧中的车道和对象传感器.一个时间表变量存储在gtdata保存地面真实数据。此变量包含用于以后分析的每帧数据。

%创建一个VideoReader对象来读取视频帧。videoName = loaded.videoName;fileReader = VideoReader (videoName);%地面实况数据按时间表组织。gtdata = loaded.groundTruthData;%显示地面真实数据的前几行。头(gtdata)
车辆LaneBoundaries ans = 8 x2时间表时间  ____________ ____________ ______________ 0秒{6 x4双}}{2 x1细胞0.033333秒{6 x4双}}{2 x1细胞0.066667秒{6 x4双}}{2 x1细胞0.1秒{6 x4双}}{2 x1细胞0.13333秒{6 x4双}}{2 x1细胞0.16667秒{6 x4双}}{2 x1细胞0.2秒{6 x4双}}{2 x1细胞0.23333秒{5 x4双}{2 x1细胞}

gtdata时间表有以下几列车辆LaneBoundaries.在每个时间戳中车辆列包含一个m × 4的车辆包围盒数组和LaneBoundaries列包含左右车道边界点的两元素单元数组。

首先,为图像帧可视化加载的地面真实数据。

阅读视频的第一帧。帧= readFrame (fileReader);%在第一帧提取所有车道点。lanePoints = gtdata.LaneBoundaries {1};在第一帧提取车辆边界框。vehicleBBox = gtdata.Vehicles {1};%叠加右车道点和车辆包围框。frame = insertMarker(frame, lanePoints{2},“X”);= insertObjectAnnotation帧(帧,“矩形”vehicleBBox,“汽车”);%在第一帧显示地面真实数据。图imshow(框架)

运行车道边界检测算法

使用视频帧和monoCamera参数,可以自动估计车道边界的位置。插图,processFrame的方法helperMonoSensor类用于检测车道边界(如parabolicLaneBoundary物体)和车辆(如[x, y,宽度,高度]包围盒矩阵)。就本例而言,这是车道边界检测“待测算法”。您可以使用相同的模式来评估自定义车道边界检测算法,其中processFrame为自定义检测功能。车辆坐标中的地面真值点也存储在兰尼森维切科德酒店gtdata时间表。这样,它们就可以在稍后的鸟瞰图中显示出来。首先,配置helperMonoSensor对象的传感器.的helperMonoSensor类集合了运行车道边界检测算法所需的所有必要步骤。

%设置monoSensorHelper处理视频。monoCameraSensor = loaded.sensor;monoSensorHelper = helperMonoSensor (monoCameraSensor);用相同的时间矢量为测量创建新的时间表。测量=时间表(gtdata.Time);为保持车道边界和车辆数据设置时间表列。numFrames =地板(fileReader.FrameRate * fileReader.Duration);测量。LaneBoundaries = cell(numFrames, 2);测量。车辆检测= cell(numFrames, 1);gtdata。LanesInVehicleCoord = cell(numFrames, 2);%将视频回放到t=0,并创建一个帧索引以保持当前状态%框架。fileReader。CurrentTime = 0;frameIndex = 0;%循环播放视频文件,直到没有新的帧。hasFrame(文件阅读器)frameIndex=frameIndex+1;frame=readFrame(文件阅读器);%使用processFrame方法计算检测。%这个方法可以被一个自定义的车道检测方法代替。detections = processFrame(monoSensorHelper,帧);%存储估计的车道边界和车辆检测。measures.LaneBoundaries{frameIndex}=[detections.leftegobundary]...detections.rightEgoBoundary];Measures.VehicleDetections{frameIndex}=detections.vehicleBoxes;%为便于比较,请将地面真实车道点转换为%车辆坐标系。gtPointsThisFrame = gtdata.LaneBoundaries {frameIndex};车辆点数= cell(1, numel(gtpointthisframe));ii=1:numel(gtPointsThisFrame)车辆点{ii}=imageToVehicle(单摄像机传感器,gtPointsThisFrame{ii});结束%存储以车辆坐标表示的地面真值点。gtdata。LanesInVehicleCoord {frameIndex} = vehiclePoints;结束

现在您已经用车道检测算法处理了视频,验证地面真值点正确地转换到车辆坐标系统。的第一个条目兰尼森维切科德酒店gtdata时间表包含第一帧的车辆坐标。在鸟瞰视图的第一帧中绘制这些地面真值点。

%倒带视频到t = 0。fileReader。CurrentTime = 0;阅读视频的第一帧。帧= readFrame (fileReader);birdsEyeImage = transformImage (monoSensorHelper。BirdsEyeConfig,框架);%在鸟瞰图中提取第一帧右车道点。firstFrameVehiclePoints = gtdata.LanesInVehicleCoord {1};pointsInBEV = vehicleToImage (monoSensorHelper。BirdsEyeConfig, firstFrameVehiclePoints {2});%在框架上叠加点。birdsEyeImage=插入标记(birdsEyeImage,pointsInBEV,“X”“大小”6);在鸟瞰视图中显示变换后的点。图imshow (birdsEyeImage)

测量检测误差

计算车道边界检测中的误差是验证几个下游子系统性能的关键步骤。这些子系统包括车道偏离警告系统,其取决于车道检测子系统的精度。

您可以通过测量拟合优度来估计精度。通过计算地面真值点和估计值,您现在可以对它们进行比较和可视化,以了解检测算法的性能。

适合度可以在每帧水平或整个视频测量。每帧统计数据提供了关于特定场景的详细信息,比如在弯道处的行为,在这里检测算法的性能可能会有所不同。全球统计数据提供了错过检测的车道数量的大致估计。

使用evaluateLaneBoundaries函数返回全局检测统计信息和作业数组中。该阵列将估计的车道边界目标与相应的地面真值点进行匹配。

中的阈值参数evaluateLaneBoundaries函数表示车辆坐标中的最大横向距离,以与估计的抛物线车道边界匹配。

阈值=0.25;%以米为单位[numMatches, numMisses, numFalsePositives, assignments] =...评估车道边界(测量值、车道基础、,...gtdata。LanesInVehicleCoord,...阈值);disp (['匹配数:',num2str(numMatches)];disp(['未命中次数:'num2str (numMisses)]);disp (['误报数:',num2str(numFalsePositives)];
匹配次数:409失败次数:36误报次数:27

使用作业数组,您可以计算有用的每车道度量,如估计值和地面真值点之间的平均横向距离。这些指标表明算法的执行情况如何。要计算平均距离度量值,请使用helper函数helperComputeLaneStatistics,它在本例的最后定义。

averageDistance=helperComputeLaneStatistics(测量值。LaneBoundaries,...gtdata。LanesInVehicleCoord,...作业,@mean);%绘制估算值与地面真实值之间的平均距离。图阻止(gtdata。时间,averageDistance) title(“估计值与地面真实值之间的平均距离”网格)ylabel (“米距离”)传奇(的左边界“右边界”

可视化和回顾地面真相和你的算法之间的差异

现在,您已经对车道检测算法的准确性有了定量的了解。但是,仅根据上一节中的绘图不可能完全理解故障。因此,查看视频并在每帧的基础上可视化错误对于识别特定故障模式至关重要,而这些故障模式可以改善通过改进算法得到验证。

您可以使用Ground Truth Labeler应用程序作为可视化工具来查看包含地面真实数据和估计车道边界的视频。的driving.connector.Connector类提供了将自定义可视化工具附加到Ground Truth label的接口。

使用parabolicLaneBoundary阵列和地面真实数据,计算估计点的车辆坐标位置。的parabolicLaneBoundary数组定义一条线,地面真值数据在道路上标出离散点。的helperGetCorrespondingPoints函数估计与车辆相同Y轴距离对应的估计线上的点。此帮助器函数在示例末尾定义。

地面真值点和估计点现在包括在一个新的时间表可以在Ground Truth Labeler应用程序中可视化groundTruth对象然后存储为MAT文件。

%使用单相机计算估计的点位置。[estVehiclePoints, estImagePoints] = helpergetcordingpoints (monocamerassensor,...测量。LaneBoundaries,...gtdata。LanesInVehicleCoord,...任务);%将估计车道添加到测量时间表中。测量。EstimatedLanes = estImagePoints;测量。LanesInVehicleCoord = estVehiclePoints;%创建一个新的时间表,包含可视化所需的所有变量。名称= {“LanePoints”“DetectedLanePoints”};类型= labelType ({“行”“行”});labelDefs=表格(名称、类型、,“变化无常”, {“名字”“类型”});(gtdata visualizeInFrame =时间表。时间,...gtdata。LaneBoundaries,...测量。EstimatedLanes,...“变化无常”、名称);%创建groundTruth对象。数据源= groundTruthDataSource (videoName);dataToVisualize = groundTruth(dataSource, labelDefs, visualizeInFrame);%将上一节的所有结果保存在distanceData.mat中的%临时文件夹。dataToLoad=[tempdir“distanceData.mat”];保存(dataToLoad,“单传感器辅助器”“videoName”“测量”“gtdata”“averageDistance”);

helperCustomUI类使用从MAT文件加载的数据创建绘图和鸟瞰视图,就像您刚才创建的那个。Ground Truth Labeler应用程序的Connector接口与helperCustomUI类通过助手连接器类将视频与平均距离图和鸟瞰图同步。此同步使您能够以分析方式和视觉方式分析每帧结果。

按照以下步骤将结果可视化,如下图所示:

  • 转到临时目录,其中distanceData.mat,并打开Ground Truth Labeler应用程序。然后启动Ground Truth Labeler应用程序,连接器手柄指定为助手连接器使用以下命令:

>> origdir = pwd;>> cd(tempdir) >> groundTruthLabeler(dataSource,“ConnectorTargetHandle”, @helperUIConnector);
  • 导入标签:在图像坐标中可视化地面真实车道标记和估计车道。从应用程序工具条,单击导入标签.然后选择从工作空间选项并加载dataToVisualize应用程序的主窗口现在包含车道标记的注释。

现在您可以浏览视频并检查错误。要返回到原始目录,可以输入:

> > cd (origdir)

从这个可视化中,您可以对算法和地面真实数据的质量做出几个推论。

  • 左车道的精度总是比右车道的精度差。在鸟瞰图显示中仔细观察,地面真实数据被标记为双线的外边界,而估计的车道边界一般位于双线标记的中心。这表明左车道估计可能比数字描述的更准确,一个明确定义的地面真值数据集对这样的观测至关重要。

  • 2.3秒和4秒左右的检测间隔对应于道路上有人行横道的十字路口。这表明该算法在存在人行横道的情况下性能不佳。

  • 大约6.8秒,当车辆接近第三个十字路口时,ego车道分流为左车道和直车道。在这里,算法也无法准确捕获左车道,地面真实数据也不包含五帧的任何信息。

结论

这个例子展示了如何测量精度的车道边界检测算法和可视化它使用地面实况贴标签机你可以将这个概念扩展到其他自定义算法,以简化这些工作流程,并扩展应用程序的功能,用于自定义测量。

金宝app辅助功能

helperComputeLaneStatistics

这个辅助函数计算车道边界检测与地面真值点的统计数据。它接受一个函数句柄,可用于泛化需要计算的统计数据,包括@mean和@median。

函数stat = helperComputeLaneStatistics(estModels, gtPoints, assignments, fcnHandle) numFrames = length(estModels);%使左和右估计NaN默认表示缺乏%的数据。stat=NaN*one(numFrames,2);frameInd = 1: numFrames%默认情况下使左右估计NaN。stat(frameInd,:) = NaN*ones(1,1); / /设置帧长度idx = 1:长度(estModels {frameInd})%忽略假阳性分配。如果赋值{frameInd}(idx)==0持续结束%estModelInFrame中的第k个边界与第k个匹配%通过gtPointsInFrame中的赋值索引的元素。thisModel=estModels{frameInd}(idx);thisGT=gtPoints{frameInd}{assignments{frameInd}(idx)};thisGTModel=driving.internal.piecewiseLinearBoundary(thisGT);如果平均值(thisGTModel.Points(:,2))>0%左车道xPoints = thisGTModel.Points (: 1);yDist = 0(大小(xPoints));索引=1:numel(xPoints)gtYPoints=thisGTModel.computeBoundaryModel(xPoints(index));testYPoints=thisModel.computeBoundaryModel(xPoints(index));yDist(index)=abs(testYPoints gtYPoints);结束stat(frameInd, 1) = fcnHandle(yDist); / /删除帧其他的%右车道xPoints = thisGTModel.Points (: 1);yDist = 0(大小(xPoints));索引=1:numel(xPoints)gtYPoints=thisGTModel.computeBoundaryModel(xPoints(index));testYPoints=thisModel.computeBoundaryModel(xPoints(index));yDist(index)=abs(testYPoints gtYPoints);结束stat(frameInd, 2) = fcnHandle(yDist); / /删除帧结束结束结束结束

helperGetCorrespondingPoints

这个辅助函数在x轴位置创建与地面真实点匹配的车辆和图像坐标点。

函数[vehiclePoints, imagePoints] = helpergetcordingpoints (monocamerassensor, estModels, gtPoints, assignments) numFrames = length(estModels);imagePoints = cell(numFrames, 1);车辆点数= cell(numFrames, 1);frameInd = 1: numFrames如果isempty(赋值{frameInd})imagePointsInFrame=[];vehiclePointsInFrame=[];其他的estModelInFrame = estModels {frameInd};gtPointsInFrame = gtPoints {frameInd};imagePointsInFrame = cell(length(estModelInFrame), 1);vehiclePointsInFrame = cell(length(estModelInFrame), 1);idx = 1:长度(estModelInFrame)%忽略假阳性分配。如果imagePointsInFrame{idx} = [NaN NaN];持续结束%estModelInFrame中的第k个边界与第k个匹配%通过gtPointsInFrame中的赋值索引的元素。这种模式下= estModelInFrame (idx);thisGT = gtPointsInFrame{作业{frameInd} (idx)};xPoints = thisGT(:, 1); / /点击这里yPoints = thisModel.computeBoundaryModel (xPoints);vehiclePointsInFrame{idx} = [xPoints, yPoints];imagePointsInFrame{idx} = vehicleToImage(monocamerassensor, [xPoints yPoints]);结束结束vehiclePoints {frameInd} = vehiclePointsInFrame;imagePoints {frameInd} = imagePointsInFrame;%使imagePoints[]而不是{}遵守groundTruth对象。如果isempty(imagePoints{frameInd})imagePoints{frameInd}=[];结束如果isempty(vehiclePoints{frameInd}) vehiclePoints{frameInd} = [];结束结束结束

另请参阅

应用程序

功能

对象

相关话题