评估和可视化车道边界检测与地面真相
这个例子展示了如何根据已知的地面真相来评估车道边界检测的性能。在本例中,您将通过计算拟合优度度量来描述车道边界检测算法在每帧基础上的性能。此度量可用于精确定位、可视化和理解底层算法中的故障模式。
概述
随着人们对基于视觉的自动驾驶问题解决方案的兴趣日益浓厚,能够评估和验证检测算法的准金宝搏官方网站确性变得非常重要。在具有多个参数的检测算法中,验证准确性尤其重要,这些参数可以调优以获得满足预定义质量要求的结果。这个示例演示了一个这样的工作流,其中可以测量车道边界的准确性。该工作流程有助于在每帧的基础上精确定位这些算法中的故障模式,以及表征其整体性能。该工作流还可以帮助您直观和定量地了解算法的性能。然后,您可以使用这种理解来调优底层算法以提高其性能。
地面真实数据
本例中使用的数据集是来自一辆行驶在街道上的车辆上的前置摄像头的视频文件。车道边界的地面真相已使用地面真相标签器应用程序在视频上手动标记,使用标记为“车道边界”的Line ROI。这个视频时长8秒,也就是250帧。它有三个十字路口,几辆车(停着的和移动的)和车道边界(双线,单线和虚线)。要为自己的视频创建地面真相车道边界数据集,可以使用地面真相标签应用程序。
加载MAT文件与地面实况数据。装载,装载“caltech_cordova1_laneAndVehicleGroundTruth.mat”);
的加载
结构包含三个字段:
groundTruthData
,一个有两栏的时间表:LaneBoundaries
而且车辆
.LaneBoundaries
包含自我车道边界(左和右)的地面真值点,表示为组成一条多线线的XY点单元格数组。车辆
包含相机视图中车辆的地面真实边界框,表示为m × 4的[x,y,宽度,高度]数组。传感器
,一个monoCamera
具有有关安装在车辆上的校准摄像机的属性的对象。这个物体可以让你估计现实世界中车辆和前方物体之间的距离。videoName
,一个字符数组,包含存储帧的视频的文件名。
从这个结构中的数据中,使用打开视频文件VideoReader
循环帧。的VideoReader
对象使用helperMonoSensor
对象来检测视频帧中的车道和对象,使用存储在传感器
.一个时间表
变量存储在gtdata
保存地面真值数据。此变量包含稍后用于分析的每帧数据。
创建一个VideoReader对象来读取视频的帧。videoName =加载。videoName;fileReader = videereader (videoName);地面实况数据被组织在一个时间表中。gtdata = loaded.groundTruthData;显示ground truth数据的前几行。头(gtdata)
ans = 8x2 Time Vehicles LaneBoundaries ____________ ____________ ______________ 0秒{6x4双胞}{2x1 cell} 0.033333秒{6x4双胞}{2x1 cell} 0.066667秒{6x4双胞}{2x1 cell} 0.1秒{6x4双胞}{2x1 cell} 0.13333秒{6x4双胞}{2x1 cell} 0.16667秒{6x4双胞}{2x1 cell} 0.2秒{6x4双胞}{2x1 cell} 0.23333秒{5x4双胞}{2x1 cell}
的gtdata
时间表有列车辆
而且LaneBoundaries
.在每个时间戳中车辆
列保存了一个m × 4的车辆包围框数组LaneBoundaries
列保存左右车道边界点的两元素单元格数组。
首先,可视化加载的图像帧的真实数据。
阅读视频的第一帧。frame = 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
替换为自定义检测功能。车辆坐标中的地面真值点也存储在LanesInVehicleCoord
的列gtdata
时间表。这样,以后就可以在鸟瞰视图中看到它们。首先,配置helperMonoSensor
对象的传感器
.的helperMonoSensor
类组装运行车道边界检测算法所需的所有必要步骤。
设置monoSensorHelper来处理视频。monocamersensor = loaded.sensor;monoSensorHelper = helperMonoSensor(monocamersensor);为测量创建具有相同时间向量的新时间表。测量=时间表(gtdata.Time);%设置时间表列,用于保存车道边界和车辆数据。numFrames = floor(fileReader.FrameRate*fileReader.Duration);测量。LaneBoundaries = cell(numFrames, 2);测量。车辆检测= cell(numFrames, 1);gtdata。LanesInVehicleCoord = cell(numFrames, 2);将视频倒回到t = 0,并创建一个帧索引来保持当前%框架。fileReader。CurrentTime = 0;frameIndex = 0;遍历视频文件,直到没有新的帧。而hasFrame(fileReader) frameIndex = frameIndex+1;frame = readFrame(fileReader);使用processFrame方法计算检测。此方法可以替换为自定义车道检测方法。detection = processFrame(monoSensorHelper, frame);存储估计的车道边界和车辆检测。测量。LaneBoundaries{frameIndex} = [detection . leftegoboundary ....detections.rightEgoBoundary];测量。VehicleDetections{frameIndex} = detects . vehicleboxes;为便于比较,将地面真值车道点转换为%车辆坐标系。gtPointsThisFrame = gtdata.LaneBoundaries{frameIndex};vehiclePoints = cell(1, numel(gtPointsThisFrame));为ii = 1:numel(gtPointsThisFrame) vehiclePoints{ii} = imageToVehicle(monocamersensor, gtPointsThisFrame{ii});结束存储以车辆坐标表示的地面真值点。gtdata。LanesInVehicleCoord{frameIndex} = vehiclePoints;结束
现在您已经使用车道检测算法处理了视频,验证地面真相点是否正确地转换为车辆坐标系统。的第一个条目LanesInVehicleCoord
的列gtdata
时间表包含第一帧的车辆坐标。在鸟瞰视图的第一帧上绘制这些地面真相点。
%倒带视频到t = 0。fileReader。CurrentTime = 0;阅读视频的第一帧。frame = readFrame(fileReader);birdsEyeImage = transformImage(monoSensorHelper。BirdsEyeConfig,框架);在鸟瞰视图中提取第一帧的右车道点。firstFrameVehiclePoints = gtdata.LanesInVehicleCoord{1};pointsInBEV = vehicleToImage(monoSensorHelper。BirdsEyeConfig, firstFrameVehiclePoints {2});在框架上叠加点。birdsEyeImage = insertMarker(birdsEyeImage, pointsInBEV,“X”,“大小”6);在鸟瞰视图中显示转换后的点。图imshow (birdsEyeImage)
测量检测误差
车道边界检测的误差计算是验证下游子系统性能的重要步骤。这些子系统包括依赖于车道检测子系统的准确性的车道偏离警告系统。
你可以通过测量拟合优度来估计这个准确度。计算出基本真相点和估计值后,您现在可以对它们进行比较和可视化,以了解检测算法的表现如何。
拟合优度既可以在每帧水平上测量,也可以在整个视频中测量。每帧统计信息提供了关于特定场景的详细信息,例如在道路拐弯处的行为,其中检测算法的性能可能会有所不同。全球统计数据提供了漏检车道数量的总体估计。
使用evaluateLaneBoundaries
函数返回全局检测统计信息作业
数组中。该数组将估计的车道边界对象与相应的地面真值点进行匹配。
中的阈值参数evaluateLaneBoundaries
函数表示车辆坐标中的最大横向距离,以与估计的抛物线车道边界匹配。
阈值= 0.25;米%[numMatches, numMisses, numFalsePositives, assignments] =...evaluateLaneBoundaries(测量。LaneBoundaries,...gtdata。LanesInVehicleCoord,...阈值);disp (['匹配数:'num2str (numMatches)]);disp ([“失手次数:”num2str (numMisses)]);disp ([“误报数量:”num2str (numFalsePositives)]);
匹配次数:405未命中次数:40误报次数:30
使用作业
数组,您可以计算有用的每车道指标,如估计值和地面真值点之间的平均横向距离。这样的指标表明算法的表现如何。要计算平均距离度量,请使用helper函数helperComputeLaneStatistics
,在本例的最后定义。
averageDistance = helpcomputelanestatistics(测量。LaneBoundaries,...gtdata。LanesInVehicleCoord,...作业,@mean);绘制估计值与实际值之间的平均距离。图阻止(gtdata。时间,averageDistance) title(“估计值与实际数据之间的平均距离”网格)在ylabel (“以米为单位的距离”)传说(的左边界,“右边界”)
可视化和审查地面真相和你的算法之间的差异
现在,您对车道检测算法的准确性有了定量的了解。然而,仅仅根据前一节中的情节是不可能完全理解这些失败的。因此,查看视频并在每帧的基础上可视化错误对于识别特定的故障模式至关重要,可以通过改进算法来改进。
您可以使用Ground Truth Labeler应用程序作为可视化工具来查看包含Ground Truth数据和估计车道边界的视频。的
类提供了将自定义可视化工具附加到Ground Truth Labeler的接口。driving.connector.Connector
使用parabolicLaneBoundary
数组和地面真相数据计算车辆坐标位置的估计值点。的parabolicLaneBoundary
数组定义一条线,地面真相数据在道路上标记有离散点。的helperGetCorrespondingPoints
函数估计估计线上的点,这些点对应于与车辆相同的y轴距离。这个helper函数在示例的末尾定义。
地面真相点和估计点现在包括在一个新的时间表
将在Ground Truth Labeler应用程序中可视化groundTruth
对象被存储为MAT文件。
使用单摄像机计算估计的点位置。[estVehiclePoints, estImagePoints] = helperGetCorrespondingPoints(monocamersensor,...测量。LaneBoundaries,...gtdata。LanesInVehicleCoord,...作业);将估计车道添加到测量时间表中。。测量。EstimatedLanes = estImagePoints;测量。lanesinvehiclepoints = estVehiclePoints;用可视化所需的所有变量创建一个新的时间表。。名称= {“LanePoints”;“DetectedLanePoints”};类型= labelType({“行”;“行”});labelDefs = table(名称,类型,“VariableNames”,{“名字”,“类型”});visualizeInFrame =时间表(gtdata. visualizeInFrame =时间表。时间,...gtdata。LaneBoundaries,...测量。EstimatedLanes,...“VariableNames”、名称);创建groundTruth对象。dataSource = groundTruthDataSource(videoName);datatovisualization = groundTruth(dataSource, labelDefs, visualizeInFrame);将上一节的所有结果保存在distanceData中。垫子%临时文件夹。dataToLoad = [tempdir .“distanceData.mat”];保存(dataToLoad,“monoSensorHelper”,“videoName”,“测量”,“gtdata”,“averageDistance”);
的helperCustomUI
类使用从MAT文件加载的数据创建plot和Bird's-Eye Views,就像刚才创建的那样。Ground Truth Labeler应用程序的连接器接口与helperCustomUI
类通过helperUIConnector
类将视频与平均距离图和鸟瞰图同步。这种同步使您能够分析每帧结果,包括分析和可视化。
按照以下步骤将结果可视化,如下图所示:
进入其中的临时目录
distanceData.mat
保存并打开Ground Truth Labeler应用程序。然后启动Ground Truth Labeler应用程序,连接器句柄指定为helperUIConnector
使用如下命令:
>> origdir = pwd;>> cd(tempdir) >>“ConnectorTargetHandle”, @helperUIConnector);
导入标签:在图像坐标中可视化真实车道标记和估计车道。在应用程序工具条中,单击进口标签.然后选择从工作空间选项,并加载
dataToVisualize
ground truth进入应用程序。主应用程序窗口现在包含车道标记的注释。
现在您可以浏览视频并检查错误。要返回到原来的目录,您可以输入:
> > cd (origdir)
从这个可视化,您可以对算法和地面真相数据的质量做出一些推断。
左车道的精度始终比右车道的精度差。在鸟瞰显示器中仔细观察,地面真实数据被标记为双线的外边界,而估计的车道边界一般位于双线标记的中心。这表明左车道估计可能比数字所描述的更准确,并且明确定义的地面真相数据集对于此类观测至关重要。
约2.3秒和4秒的检测间隔对应于道路上有人行横道的十字路口。这表明该算法在人行横道存在时表现不佳。
6.8秒左右,当车辆驶近第三个十字路口时,“自我车道”分为“左车道”和“直车道”。在这里,算法同样不能准确地捕获左车道,地面真相数据也不包含任何信息的五帧。
结论
该示例展示了如何测量车道边界检测算法的准确性,并使用可视化方法将其可视化地面真相标签您可以将此概念扩展到其他自定义算法,以简化这些工作流,并扩展应用程序的自定义测量功能。
金宝app支持功能
helperComputeLaneStatistics
这个辅助函数计算车道边界检测的统计信息,与地面真值点相比。它接受一个函数句柄,可用于泛化需要计算的统计数据,包括@mean和@median。
函数stat = helpcomputelanestatistics (estModels, gtPoints, assignments, fcnHandle) numFrames = length(estModels);默认情况下,左右估计NaN表示缺乏%的数据。stat = NaN*ones(numFrames, 2);为frameInd = 1:numFrames默认情况下进行左右估计。stat(frameInd,:) = NaN*ones(2,1);为idx = 1:length(estModels{frameInd})忽略错误的阳性分配。如果分配{frameInd}(idx) == 0继续;结束estModelInFrame中的第k个边界与第k个边界匹配在gtPointsInFrame中由赋值索引的元素。thisModel = estModels{frameInd}(idx);thisGT = gtPoints{frameInd}{assignments{frameInd}(idx)};thisGT = driving.internal.piecewiseLinearBoundary(thisGT);如果mean(thisGTModel.Points(:,2)) > 0%左车道xPoints = thisGTModel.Points(:,1);yDist = 0 (size(xPoints));为索引= 1:数字(xPoints) gtYPoints = thisGTModel.computeBoundaryModel(xPoints(索引));testYPoints = thisModel.computeBoundaryModel(xPoints(index));yDist(index) = abs(testYPoints-gtYPoints);结束stat(frameInd, 1) = fcnHandle(yDist);其他的%右车道xPoints = thisGTModel.Points(:,1);yDist = 0 (size(xPoints));为索引= 1:数字(xPoints) gtYPoints = thisGTModel.computeBoundaryModel(xPoints(索引));testYPoints = thisModel.computeBoundaryModel(xPoints(index));yDist(index) = abs(testYPoints-gtYPoints);结束stat(frameInd, 2) = fcnHandle(yDist);结束结束结束结束
helperGetCorrespondingPoints
这个辅助函数在x轴位置上创建与地面真值点匹配的车辆和图像坐标点。
函数[vehiclePoints, imagePoints] = helperGetCorrespondingPoints(monocamersensor, estModels, gtPoints, assignments) numFrames = length(estModels);imagePoints = cell(numFrames, 1);vehiclePoints = cell(numFrames, 1);为frameInd = 1:numFrames如果isempty(assignments{frameInd}) imagePointsInFrame = [];vehiclePointsInFrame = [];其他的estModelInFrame = estModels{frameInd};gtPointsInFrame = gtPoints{frameInd};imagePointsInFrame = cell(length(estModelInFrame), 1);vehiclePointsInFrame = cell(length(estModelInFrame), 1);为idx = 1:length(estModelInFrame)忽略错误的阳性分配。如果分配{frameInd}(idx) == 0 imagePointsInFrame{idx} = [NaN NaN];继续;结束estModelInFrame中的第k个边界与第k个边界匹配在gtPointsInFrame中由赋值索引的元素。thisModel = estModelInFrame(idx);thisGT = gtPointsInFrame{assignments{frameInd}(idx)};xPoints = thisGT(:, 1);yPoints = thisModel.computeBoundaryModel(xPoints);vehiclePointsInFrame{idx} = [xPoints, yPoints];imagePointsInFrame{idx} = vehicleToImage(monocamersensor, [xPoints yPoints]);结束结束vehiclePoints{frameInd} = vehiclePointsInFrame;imagePoints{frameInd} = imagePointsInFrame;使用imagePoints[]代替{}来符合groundTruth对象。如果isempty(imagePoints{frameInd}) imagePoints{frameInd} = [];结束如果isempty(vehiclePoints{frameInd}) vehiclePoints{frameInd} = [];结束结束结束