Этот пример показывает, как оценить производительность обнаружения контура маршрута против известной наземной истины. В этом примере вы охарактеризуете производительность алгоритма обнаружения контура маршрута на основе на кадр путем вычисления меры качества подгонки. Эта мера может использоваться, чтобы точно определить, визуализировать, и понять типы отказа в базовом алгоритме.
С возрастающим интересом к основанным на видении решениям автоматизированных ведущих проблем способность оценить и проверить точность алгоритмов обнаружения стала очень важной. Проверка точности особенно важна в алгоритмах обнаружения, которые имеют несколько параметров, которые могут быть настроены, чтобы достигнуть результатов, которые удовлетворяют предопределенные требования к уровню качества. Этот пример идет через один такой рабочий процесс, где контуры маршрута могут быть измерены для их уровня точности. Этот рабочий процесс помогает точно определить типы отказа в этих алгоритмах на основе на кадр, а также охарактеризовать его общую производительность. Этот рабочий процесс также помогает вам визуально, и количественно поймите производительность алгоритма. Можно затем использовать это понимание, чтобы настроить базовый алгоритм, чтобы улучшать его производительность.
Набор данных, используемый в этом примере, является видеофайлом от фронтальной камеры на автомобиле, проезжающем улица. Основывайтесь истина для контуров маршрута была вручную отмечена на видео с приложением Ground Truth Labeler, использование ROI Строки маркировало "LaneBoundary". Это видео составляет 8 секунд или 250 кадров долго. Это имеет три перекрестных пересечения, несколько автомобилей (припаркованный и перемещающийся), и контуры маршрута (двойная строка, одна, и подчеркнутая штриховой линией). Чтобы создать наземный набор данных контура маршрута истины для вашего собственного видео, можно использовать приложение Ground Truth Labeler.
% Load MAT file with ground truth data loaded = load('caltech_cordova1_laneAndVehicleGroundTruth.mat');
Структура loaded
содержит три поля:
groundTruthData
, расписание с двумя столбцами: LaneBoundaries
и Vehicles
. LaneBoundaries
содержит наземные точки истины для контуров маршрута эго (левых и правых), представленных как массив ячеек точек XY, формирующих poly строку. Vehicles
содержит наземные ограничительные рамки истины для автомобилей в поле зрения камеры, представленном как массивы M-4 [x, y, ширина, высота].
sensor
, объект monoCamera
со свойствами о калиброванной камере, смонтированной на автомобиле. Этот объект позволяет вам оценить реальные расстояния между автомобилем и объектами перед ним.
videoName
, символьный массив, содержащий имя файла видео, где кадры хранятся.
От данных в этой структуре откройте видеофайл при помощи VideoReader
, чтобы циклично выполниться через кадры. Объект VideoReader
использует объект helperMonoSensor
обнаружить маршруты и объекты в кадре видео, с помощью настройки камеры, сохраненной в sensor
. Переменная timetable
, сохраненная в gtdata
, содержит наземные данные об истине. Эта переменная содержит данные на кадр, которые используются для анализа позже.
% Create a VideoReader object to read frames of the video. videoName = loaded.videoName; fileReader = VideoReader(videoName); % The ground truth data is organized in a timetable. gtdata = loaded.groundTruthData; % Display the first few rows of the ground truth data. head(gtdata)
ans = 8x2 timetable Time Vehicles LaneBoundaries ____________ ____________ ______________ 0 sec [6x4 double] {2x1 cell} 0.033333 sec [6x4 double] {2x1 cell} 0.066667 sec [6x4 double] {2x1 cell} 0.1 sec [6x4 double] {2x1 cell} 0.13333 sec [6x4 double] {2x1 cell} 0.16667 sec [6x4 double] {2x1 cell} 0.2 sec [6x4 double] {2x1 cell} 0.23333 sec [5x4 double] {2x1 cell}
Расписание gtdata
имеет столбцы Vehicles
и LaneBoundaries
. В каждой метке времени столбец Vehicles
содержит массив M-4 ограничительных рамок автомобиля, и столбец LaneBoundaries
содержит двухэлементный массив ячеек левых и правых граничных точек маршрута.
Во-первых, визуализируйте загруженные наземные данные об истине для фрейма изображения.
% Read the first frame of the video. frame = readFrame(fileReader); % Extract all lane points in the first frame. lanePoints = gtdata.LaneBoundaries{1}; % Extract vehicle bounding boxes in the first frame. vehicleBBox = gtdata.Vehicles{1}; % Superimpose the right lane points and vehicle bounding boxes. frame = insertMarker(frame, lanePoints{2}, 'X'); frame = insertObjectAnnotation(frame, 'rectangle', vehicleBBox, 'Vehicle'); % Display ground truth data on the first frame. figure imshow(frame)
Используя кадры видео и параметры monoCamera
, можно автоматически оценить местоположения контуров маршрута. Для рисунка метод processFrame
класса helperMonoSensor
используется здесь, чтобы обнаружить контуры маршрута (когда parabolicLaneBoundary
возражает), и автомобили (как [x, y, ширина, высота] матрицы ограничительной рамки). В целях этого примера это - алгоритм "обнаружения контура маршрута под тестом". Можно использовать тот же шаблон для оценки пользовательского алгоритма обнаружения контура маршрута, где processFrame
заменяется пользовательской функцией обнаружения. Наземные точки истины в координатах автомобиля также хранятся в столбце LanesInVehicleCoord
расписания gtdata
. Тем путем они могут визуализироваться в отображении Вида с высоты птичьего полета позже. Во-первых, сконфигурируйте объект helperMonoSensor
с sensor
. Класс helperMonoSensor
собирает все необходимые шаги, требуемые запускать алгоритм обнаружения контура маршрута.
% Set up monoSensorHelper to process video. monoCameraSensor = loaded.sensor; monoSensorHelper = helperMonoSensor(monoCameraSensor); % Create new timetable with same Time vector for measurements. measurements = timetable(gtdata.Time); % Set up timetable columns for holding lane boundary and vehicle data. numFrames = floor(fileReader.FrameRate*fileReader.Duration); measurements.LaneBoundaries = cell(numFrames, 2); measurements.VehicleDetections = cell(numFrames, 1); gtdata.LanesInVehicleCoord = cell(numFrames, 2); % Rewind the video to t = 0, and create a frame index to hold current % frame. fileReader.CurrentTime = 0; frameIndex = 0; % Loop through the videoFile until there are no new frames. while hasFrame(fileReader) frameIndex = frameIndex+1; frame = readFrame(fileReader); % Use the processFrame method to compute detections. % This method can be replaced with a custom lane detection method. detections = processFrame(monoSensorHelper, frame); % Store the estimated lane boundaries and vehicle detections. measurements.LaneBoundaries{frameIndex} = [detections.leftEgoBoundary ... detections.rightEgoBoundary]; measurements.VehicleDetections{frameIndex} = detections.vehicleBoxes; % To facilitate comparison, convert the ground truth lane points to the % vehicle coordinate system. gtPointsThisFrame = gtdata.LaneBoundaries{frameIndex}; vehiclePoints = cell(1, numel(gtPointsThisFrame)); for ii = 1:numel(gtPointsThisFrame) vehiclePoints{ii} = imageToVehicle(monoCameraSensor, gtPointsThisFrame{ii}); end % Store ground truth points expressed in vehicle coordinates. gtdata.LanesInVehicleCoord{frameIndex} = vehiclePoints; end
Теперь, когда вы обработали видео с алгоритмом обнаружения маршрута, проверьте, что наземные точки истины правильно преобразовываются в систему координат автомобиля. Первая запись в столбце LanesInVehicleCoord
расписания gtdata
содержит координаты автомобиля для первого кадра. Постройте эти наземные точки истины на первом кадре в Виде с высоты птичьего полета.
% Rewind video to t = 0. fileReader.CurrentTime = 0; % Read the first frame of the video. frame = readFrame(fileReader); birdsEyeImage = transformImage(monoSensorHelper.BirdsEyeConfig, frame); % Extract right lane points for the first frame in Bird's-Eye View. firstFrameVehiclePoints = gtdata.LanesInVehicleCoord{1}; pointsInBEV = vehicleToImage(monoSensorHelper.BirdsEyeConfig, firstFrameVehiclePoints{2}); % Superimpose points on the frame. birdsEyeImage = insertMarker(birdsEyeImage, pointsInBEV, 'X', 'Size', 6); % Display transformed points in Bird's-Eye View. figure imshow(birdsEyeImage)
Вычисление ошибок в обнаружении контура маршрута является существенным шагом в проверке производительности нескольких нисходящих подсистем. Такие подсистемы включают системы предупреждения о сходе с полосы, которые зависят от точности подсистемы обнаружения маршрута.
Можно оценить эту точность путем измерения качества подгонки. С наземными точками истины и вычисленными оценками, можно теперь сравнить и визуализировать их, чтобы узнать, как хорошо алгоритмы обнаружения выполняют.
Качество подгонки может быть измерено или на уровне на кадр или для целого видео. Статистические данные на кадр предоставляют подробную информацию об определенных сценариях, таких как поведение в дорожных изгибах, где производительность алгоритма обнаружения может отличаться. Глобальные статистические данные обеспечивают большую оценку изображения количества маршрутов, которые пропустили обнаружение.
Используйте функцию evaluateLaneBoundaries
, чтобы возвратить глобальную статистику обнаружения и массив assignments
. Этот массив соответствует, предполагаемые объекты контура маршрута с соответствующей наземной истиной указывает.
Пороговый параметр в функции evaluateLaneBoundaries
представляет максимальное боковое расстояние в координатах автомобиля, чтобы квалифицировать как соответствие с предполагаемыми параболическими контурами маршрута.
threshold = 0.25; % in meters [numMatches, numMisses, numFalsePositives, assignments] = ... evaluateLaneBoundaries(measurements.LaneBoundaries, ... gtdata.LanesInVehicleCoord, ... threshold); disp(['Number of matches: ', num2str(numMatches)]); disp(['Number of misses: ', num2str(numMisses)]); disp(['Number of false positives: ', num2str(numFalsePositives)]);
Number of matches: 316 Number of misses: 129 Number of false positives: 27
Используя массив assignments
, можно вычислить полезные метрики на маршрут, такие как среднее боковое расстояние между оценками и наземными точками истины. Такие метрики указывают, как хорошо алгоритм выполняет. Чтобы вычислить среднюю метрику расстояния, используйте функцию помощника helperComputeLaneStatistics
, который задан в конце этого примера.
averageDistance = helperComputeLaneStatistics(measurements.LaneBoundaries, ... gtdata.LanesInVehicleCoord, ... assignments, @mean); % Plot average distance between estimates and ground truth. figure stem(gtdata.Time, averageDistance) title('Average Distance Between Estimates and Ground Truth') grid on ylabel('Distance in Meters') legend('Left Boundary','Right Boundary')
У вас теперь есть количественное понимание точности алгоритма обнаружения маршрута. Однако не возможно полностью понять отказы только на основе графика в предыдущем разделе. Смотрение видео и визуализация ошибок на основе на кадр поэтому крайне важны для идентификации определенных типов отказа, которые могут быть улучшены путем совершенствования алгоритма.
Можно использовать приложение Ground Truth Labeler в качестве инструмента визуализации, чтобы смотреть видео, содержащее наземные данные об истине и предполагаемые контуры маршрута. Класс
обеспечивает интерфейс, чтобы присоединить пользовательские инструменты визуализации к Ground Truth Labeler.driving.connector.Connector
Используйте массив parabolicLaneBoundary
и наземные данные об истине, чтобы вычислить местоположения координаты автомобиля предполагаемых точек. Массив parabolicLaneBoundary
задает строку, и наземным данным об истине отметили дискретные точки на дороге. Функция helperGetCorrespondingPoints
оценивает точки на предполагаемых строках, которые соответствуют тому же расстоянию Оси Y от автомобиля. Эта функция помощника задана в конце примера.
Наземные точки истины и предполагаемые точки теперь включены в новый timetable
, который будет визуализироваться в приложении Ground Truth Labeler. Созданный объект groundTruth
затем хранится как файл MAT.
% Compute the estimated point locations using the monoCamera. [estVehiclePoints, estImagePoints] = helperGetCorrespondingPoints(monoCameraSensor, ... measurements.LaneBoundaries, ... gtdata.LanesInVehicleCoord, ... assignments); % Add estimated lanes to the measurements timetable. measurements.EstimatedLanes = estImagePoints; measurements.LanesInVehicleCoord = estVehiclePoints; % Create a new timetable with all the variables needed for visualization. names = {'LanePoints'; 'DetectedLanePoints'}; types = labelType({'Line'; 'Line'}); labelDefs = table(names, types, 'VariableNames', {'Name','Type'}); visualizeInFrame = timetable(gtdata.Time, ... gtdata.LaneBoundaries, ... measurements.EstimatedLanes, ... 'VariableNames', names); % Create groundTruth object. dataSource = groundTruthDataSource(videoName); dataToVisualize = groundTruth(dataSource, labelDefs, visualizeInFrame); % Save all the results of the previous section in distanceData.mat in a % temporary folder. dataToLoad = [tempdir 'distanceData.mat']; save(dataToLoad, 'monoSensorHelper', 'videoName', 'measurements', 'gtdata', 'averageDistance');
Класс helperCustomUI
создает график и Виды с высоты птичьего полета с помощью данных, загруженных из файла MAT, как тот, который вы только создали. Интерфейс Connector приложения Ground Truth Labeler взаимодействует с классом helperCustomUI
через класс helperUIConnector
, чтобы синхронизировать видео со средним графиком расстояния и Видом с высоты птичьего полета. Эта синхронизация позволяет вам анализировать результаты на кадр и аналитически и визуально.
Выполните эти шаги, чтобы визуализировать результаты как показано в изображениях, которые следуют:
Перейдите к временной директории, где distanceData.mat
сохранен, и откройте приложение Ground Truth Labeler. Затем запустите приложение Ground Truth Labeler с указателем коннектора, заданным как helperUIConnector
с помощью следующих команд:
>> origdir = pwd;
>> cd(tempdir)
>> groundTruthLabeler(dataSource, 'ConnectorTargetHandle', @helperUIConnector);
Метки импорта: Визуализируйте наземные маркеры маршрута истины и предполагаемые маршруты в координатах изображений. От панели инструментов приложения нажмите Import Labels. Затем выберите Из опции Рабочей области и загрузите наземную истину dataToVisualize
в приложение. Окно главного приложения теперь содержит аннотации для маркеров маршрута.
Можно теперь перейти через видео и исследовать ошибки. Чтобы возвратиться назад к исходной директории, можно ввести:
>> cd(origdir)
От этой визуализации можно сделать несколько выводов об алгоритме и качестве наземных данных об истине.
Левая точность маршрута последовательно хуже, чем правильная точность маршрута. После более близкого наблюдения в отображении Вида с высоты птичьего полета наземные данные об истине отмечены как внешняя граница двойной строки, тогда как предполагаемый контур маршрута обычно лежит в центре двойного маркера строки. Это указывает, что левая оценка маршрута, вероятно, более точна, чем числа изображают, и что ясно заданный наземный набор данных истины крайне важен для таких наблюдений.
Обнаружение разрывает приблизительно 2,3 секунды, и 4 секунды соответствуют пересечениям на дороге, которым предшествуют переходы. Это указывает, что алгоритм не выполняет хорошо в присутствии переходов.
Приблизительно 6,8 секунд, когда автомобиль приближается к третьему пересечению, маршрут эго, отличаются в лево-единственный маршрут и прямой маршрут. Здесь также, алгоритму не удается получить левый маршрут точно, и наземные данные об истине также не содержат информации для пяти кадров.
Этот пример показал, как измерить точность алгоритма обнаружения контура маршрута и визуализировать ее с помощью приложения Ground Truth Labeler. Можно расширить эту концепцию к другим пользовательским алгоритмам, чтобы упростить эти рабочие процессы и расширить функциональность приложения для пользовательских измерений.
helperComputeLaneStatistics
Эта функция помощника вычисляет статистику для обнаружений контура маршрута, как сравнено, чтобы основать точки истины. Это берет в указателе на функцию, который может использоваться, чтобы обобщить статистическую величину, которая должна быть вычислена, включая @mean и @median.
function stat = helperComputeLaneStatistics(estModels, gtPoints, assignments, fcnHandle) numFrames = length(estModels); % Make left and right estimates NaN by default to represent lack of % data. stat = NaN*ones(numFrames, 2); for frameInd = 1:numFrames % Make left and right estimates NaN by default. stat(frameInd, :) = NaN*ones(2, 1); for idx = 1:length(estModels{frameInd}) % Ignore false positive assignments. if assignments{frameInd}(idx) == 0 continue; end % The kth boundary in estModelInFrame is matched to kth % element indexed by assignments in gtPointsInFrame. thisModel = estModels{frameInd}(idx); thisGT = gtPoints{frameInd}{assignments{frameInd}(idx)}; thisGTModel = driving.internal.piecewiseLinearBoundary(thisGT); if mean(thisGTModel.Points(:,2)) > 0 % left lane xPoints = thisGTModel.Points(:,1); yDist = zeros(size(xPoints)); for index = 1:numel(xPoints) gtYPoints = thisGTModel.computeBoundaryModel(xPoints(index)); testYPoints = thisModel.computeBoundaryModel(xPoints(index)); yDist(index) = abs(testYPoints-gtYPoints); end stat(frameInd, 1) = fcnHandle(yDist); else % right lane xPoints = thisGTModel.Points(:,1); yDist = zeros(size(xPoints)); for index = 1:numel(xPoints) gtYPoints = thisGTModel.computeBoundaryModel(xPoints(index)); testYPoints = thisModel.computeBoundaryModel(xPoints(index)); yDist(index) = abs(testYPoints-gtYPoints); end stat(frameInd, 2) = fcnHandle(yDist); end end end end
helperGetCorrespondingPoints
Эта функция помощника создает автомобиль и точки координаты изображений в местоположениях Оси X, которые совпадают с наземными точками истины.
function [vehiclePoints, imagePoints] = helperGetCorrespondingPoints(monoCameraSensor, estModels, gtPoints, assignments) numFrames = length(estModels); imagePoints = cell(numFrames, 1); vehiclePoints = cell(numFrames, 1); for frameInd = 1:numFrames if isempty(assignments{frameInd}) imagePointsInFrame = []; vehiclePointsInFrame = []; else estModelInFrame = estModels{frameInd}; gtPointsInFrame = gtPoints{frameInd}; imagePointsInFrame = cell(length(estModelInFrame), 1); vehiclePointsInFrame = cell(length(estModelInFrame), 1); for idx = 1:length(estModelInFrame) % Ignore false positive assignments. if assignments{frameInd}(idx) == 0 imagePointsInFrame{idx} = [NaN NaN]; continue; end % The kth boundary in estModelInFrame is matched to kth % element indexed by assignments in gtPointsInFrame. thisModel = estModelInFrame(idx); thisGT = gtPointsInFrame{assignments{frameInd}(idx)}; xPoints = thisGT(:, 1); yPoints = thisModel.computeBoundaryModel(xPoints); vehiclePointsInFrame{idx} = [xPoints, yPoints]; imagePointsInFrame{idx} = vehicleToImage(monoCameraSensor, [xPoints yPoints]); end end vehiclePoints{frameInd} = vehiclePointsInFrame; imagePoints{frameInd} = imagePointsInFrame; % Make imagePoints [] instead of {} to comply with groundTruth object. if isempty(imagePoints{frameInd}) imagePoints{frameInd} = []; end if isempty(vehiclePoints{frameInd}) vehiclePoints{frameInd} = []; end end end