В этом примере показано, как оценить эффективность обнаружения границ полосы движения на основе известной истинности грунта. В этом примере производительность алгоритма обнаружения границ полосы движения на основе каждого кадра определяется вычислением показателя соответствия. Эта мера может использоваться для определения, визуализации и понимания режимов отказа в базовом алгоритме.
С ростом интереса к решениям на основе зрения для автоматических проблем вождения, возможность оценки и проверки точности алгоритмов обнаружения стала очень важной. Проверка точности особенно важна в алгоритмах обнаружения, которые имеют несколько параметров, которые могут быть настроены для достижения результатов, удовлетворяющих заранее определенным требованиям к качеству. В этом примере рассматривается один такой рабочий процесс, в котором границы полос могут быть измерены по уровню их точности. Этот рабочий процесс помогает определить режимы отказов в этих алгоритмах на основе каждого кадра, а также охарактеризовать его общую производительность. Этот рабочий процесс также помогает визуально и количественно понять производительность алгоритма. Это понимание можно использовать для настройки базового алгоритма для повышения его производительности.
Набор данных, используемый в этом примере, представляет собой видеофайл с фронтальной камеры на транспортном средстве, проезжающем по улице. На видео с помощью приложения Ground Truth Labeler была вручную отмечена достоверность границ полосы движения с помощью ROI линии с меткой «Граница полосы». Это видео длиной 8 секунд, или 250 кадров. Он имеет три перекрестка, несколько транспортных средств (припаркованных и движущихся) и границы полос (двойная линия, одинарная и пунктирная). Чтобы создать набор данных границы полосы для собственного видео, можно использовать приложение Ground Truth Labeler.
% Load MAT file with ground truth data loaded = load('caltech_cordova1_laneAndVehicleGroundTruth.mat');
loaded структура содержит три поля:
groundTruthData, расписание с двумя колонками: LaneBoundaries и Vehicles. LaneBoundaries содержит точки истинности земли для границ эго-полосы (слева и справа), представленные в виде массива ячеек точек XY, образующих полилинию. Vehicles содержит ограничительные рамки грунта для транспортных средств в виде камеры, представленные M-by-4 виде массивов [x, y, width, height].
sensor, a monoCamera объект со свойствами о калиброванной камере, установленный на транспортном средстве. Этот объект позволяет оценить реальные расстояния между транспортным средством и объектами перед ним.
videoNameсимвольный массив, содержащий имя файла видео, в котором хранятся кадры.
Из данных в этой структуре откройте видеофайл с помощью VideoReader для закольцовывания кадров. VideoReader объект использует helperMonoSensor объект для обнаружения полос движения и объектов в видеокадре с помощью настройки камеры, сохраненной в sensor. A 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-by-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, width, height] ограничивающей рамки). Для целей этого примера это «тестируемый алгоритм» обнаружения границ полосы движения. Можно использовать тот же самый образец для оценки пользовательского алгоритма обнаружения границ полосы движения, где processFrame заменяется пользовательской функцией обнаружения. Наземные точки истины в координатах транспортного средства также хранятся в LanesInVehicleCoord в столбце gtdata расписание. Таким образом, они могут быть визуализированы в Bird 's-Eye View позже. Сначала сконфигурируйте 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: 321 Number of misses: 124 Number of false positives: 25
Использование 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 будет визуализирован в приложении Лейбл Истины. 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 класс для синхронизации видео с графиком среднего расстояния и Bird 's-Eye View. Эта синхронизация позволяет анализировать результаты по кадрам как аналитически, так и визуально.
Выполните следующие действия для визуализации результатов, как показано на следующих изображениях:
Перейдите во временный каталог, где distanceData.mat сохраняется и открывает приложение Ground Truth Labeler. Затем запустите приложение Ground Truth Labeler с дескриптором соединителя, указанным как helperUIConnector с помощью следующих команд:
>> origdir = pwd;
>> cd(tempdir)
>> groundTruthLabeler(dataSource, 'ConnectorTargetHandle', @helperUIConnector);
Импорт меток: визуализация маркеров полосы движения и расчетных полос в координатах изображения. На панели инструментов приложения щелкните Импорт меток. Затем выберите опцию Из рабочей области (From Workspace) и загрузите dataToVisualize В главном окне приложения теперь содержатся аннотации для маркеров полос движения.
Теперь можно перемещаться по видео и проверять ошибки. Чтобы вернуться в исходный каталог, введите:
>> cd(origdir)



Из этой визуализации можно сделать несколько выводов об алгоритме и качестве данных истинности земли.
Точность в левой полосе неизменно хуже точности в правой полосе. При более тщательном наблюдении на дисплее Bird 's-Eye View данные о истинности земли помечаются как внешняя граница двойной линии, в то время как расчетная граница полосы обычно располагается в центре маркера двойной линии. Это указывает на то, что оценка левой полосы движения, вероятно, более точна, чем отображаемые цифры, и что четко определенный набор данных истинности земли имеет решающее значение для таких наблюдений.
Промежутки обнаружения около 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