Этот пример показывает, как создать монокулярную симуляцию датчика камеры, способную к контуру маршрута и обнаружениям автомобиля. Датчик сообщит об этих обнаружениях в системе координат автомобиля. В этом примере вы узнаете о системе координат, используемой Automated Driving Toolbox™ и методами компьютерного зрения, вовлеченными в проект демонстрационного монокулярного датчика камеры.
Автомобили, которые содержат функции ADAS или разработаны, чтобы быть полностью автономными, полагаются на несколько датчиков. Эти датчики могут включать гидролокатор, радар, лазерный дальномер и камеры. Этот пример иллюстрирует некоторые концепции, вовлеченные в проект монокулярной системы камеры. Такой датчик может выполнить много задач, включая:
Обнаружение контура маршрута
Обнаружение автомобилей, людей и других объектов
Оценка расстояния от автомобиля, оборудованного датчиком до препятствий
Впоследствии, показания, возвращенные монокулярным датчиком камеры, могут использоваться, чтобы выпустить предупреждения о сходе с полосы, предупреждения столкновения, или разработать маршрут сохраняют, помогают системе управления. В сочетании с другими датчиками это может также использоваться, чтобы реализовать систему экстренного торможения и другие критические по отношению к безопасности функции.
Пример реализует подмножество функций, найденных в полностью разработанной монокулярной системе камеры. Это обнаруживает контуры маршрута и задние части автомобилей, и сообщает об их местоположениях в системе координат автомобиля.
Знание внутренних и внешних калибровочных параметров камеры очень важно для точного преобразования между координатами автомобиля и пикселем.
Запустите путем определения внутренних параметров камеры. Параметры ниже были определены более раннее использование процедуры калибровки фотоаппарата, которая использовала калибровочный шаблон шахматной доски. Можно использовать приложение Camera Calibrator, чтобы получить их для камеры.
focalLength = [309.4362, 344.2161]; % [fx, fy] in pixel units principalPoint = [318.9034, 257.5352]; % [cx, cy] optical center in pixel coordinates imageSize = [480, 640]; % [nrows, mcols]
Обратите внимание на то, что коэффициенты искажения объектива были проигнорированы, потому что существует мало искажения в данных. Параметры хранятся в объекте cameraIntrinsics
.
camIntrinsics = cameraIntrinsics(focalLength, principalPoint, imageSize);
Затем, задайте ориентацию камеры относительно шасси автомобиля. Вы будете использовать эту информацию, чтобы установить камеру extrinsics, которые задают положение 3-D системы координат камеры относительно системы координат автомобиля.
height = 2.1798; % mounting height in meters from the ground pitch = 14; % pitch of the camera in degrees
Вышеупомянутые количества могут быть выведены из матриц вращения и перевода, возвращенных функцией extrinsics
. Подача задает наклон камеры от горизонтального положения. Для камеры, используемой в этом примере, список и отклонение от курса датчика являются оба нулем. Целая настройка, задающая intrinsics и extrinsics, хранится в объекте monoCamera
.
sensor = monoCamera(camIntrinsics, height, 'Pitch', pitch);
Обратите внимание на то, что объект monoCamera
настраивает очень определенную систему координат автомобиля, где Ось X указывает вперед от автомобиля, точек Оси Y слева от автомобиля, и ось Z подчеркивает от земли.
По умолчанию источник системы координат находится на земле, непосредственно ниже центра камеры, заданного центром камеры. Источник может быть перемещен при помощи свойства monoCamera
объекта monoCamera
. Кроме того, monoCamera
обеспечивает imageToVehicle
и методы vehicleToImage
для преобразования между системами координат автомобиля и изображением.
Примечание: преобразование между системами координат принимает плоскую дорогу. Это основано на установлении матрицы homography, которая сопоставляет местоположения на плоскости обработки изображений к местоположениям на дорожном покрытии. Неплоские дороги вводят ошибки в вычислениях расстояния, особенно в местоположениях, которые далеки от автомобиля.
Прежде, чем обработать целое видео, обработайте один кадр видео, чтобы проиллюстрировать концепции, вовлеченные в проект монокулярного датчика камеры.
Запустите путем создания объекта VideoReader
, который открывает видеофайл. Чтобы быть эффективной памятью, VideoReader
загружает один кадр видео за один раз.
videoName = 'caltech_cordova1.avi';
videoReader = VideoReader(videoName);
Считайте интересный кадр, который содержит маркеры маршрута и автомобиль.
timeStamp = 0.06667; % time from the beginning of the video videoReader.CurrentTime = timeStamp; % point to the chosen frame frame = readFrame(videoReader); % read frame at timeStamp seconds imshow(frame) % display frame
Примечание: Этот пример игнорирует искажение объектива. Если бы вы были обеспокоены ошибками в измерениях расстояния, введенных искажением объектива, в этой точке вы использовали бы функцию undistortImage
, чтобы удалить искажение объектива.
Существует много способов сегментировать и обнаружить маркеры маршрута. Один подход включает использование изображения вида с высоты птичьего полета, преобразовывают. Несмотря на то, что это несет вычислительные расходы, этот преобразовывают предложения одно главное преимущество. Маркеры маршрута в виде с высоты птичьего полета имеют универсальную толщину, таким образом упрощая процесс сегментации. Маркеры маршрута, принадлежащие тому же маршруту также, становятся параллельными, таким образом делая последующий анализ легче.
Учитывая настройку камеры, объект birdsEyeView
преобразовывает оригинальное изображение к виду с высоты птичьего полета. Этот объект позволяет вам задать область, что вы хотите преобразовать координаты автомобиля использования. Обратите внимание на то, что модули координаты автомобиля были установлены объектом monoCamera
, когда высота монтирования камеры была задана в метрах. Например, если бы высота была задана в миллиметрах, остальная часть симуляции использовала бы миллиметры.
% Using vehicle coordinates, define area to transform distAheadOfSensor = 30; % in meters, as previously specified in monoCamera height input spaceToOneSide = 6; % all other distance quantities are also in meters bottomOffset = 3; outView = [bottomOffset, distAheadOfSensor, -spaceToOneSide, spaceToOneSide]; % [xmin, xmax, ymin, ymax] imageSize = [NaN, 250]; % output image width in pixels; height is chosen automatically to preserve units per pixel ratio birdsEyeConfig = birdsEyeView(sensor, outView, imageSize);
Сгенерируйте изображение вида с высоты птичьего полета.
birdsEyeImage = transformImage(birdsEyeConfig, frame); figure imshow(birdsEyeImage)
Области еще дальше от датчика являются более расплывчатыми, из-за наличия меньшего количества пикселей и таким образом требования большего объема интерполяции.
Обратите внимание на то, что можно завершить последние шаги обработки без использования вида с высоты птичьего полета, пока можно определить местоположение кандидата контура маршрута пиксели в координатах автомобиля.
Имея изображение вида с высоты птичьего полета, можно теперь использовать функцию segmentLaneMarkerRidge
, чтобы разделить кандидата маркера маршрута пиксели от дорожного покрытия. Этот метод был выбран для его простоты и относительной эффективности. Альтернативные методы сегментации существуют включая семантическую сегментацию (глубокое обучение) и управляемые фильтры. Можно заменить этими методами ниже, чтобы получить бинарную маску, необходимую для следующего этапа.
Параметры входа Most к функциям ниже заданы в мировых модулях, например, ширина маркера маршрута, поданная в segmentLaneMarkerRidge
. Использование мировых модулей позволяет вам легко пробовать новые датчики, даже когда входной размер изображения изменяется. Это очень важно для создания проекта, более устойчивого и гибкого относительно изменяющегося оборудования камеры и обработки переменных стандартов по многим странам.
% Convert to grayscale birdsEyeImage = rgb2gray(birdsEyeImage); % Lane marker segmentation ROI in world units vehicleROI = outView - [-1, 2, -3, 3]; % look 3 meters to left and right, and 4 meters ahead of the sensor approxLaneMarkerWidthVehicle = 0.25; % 25 centimeters % Detect lane features laneSensitivity = 0.25; birdsEyeViewBW = segmentLaneMarkerRidge(birdsEyeImage, birdsEyeConfig, approxLaneMarkerWidthVehicle,... 'ROI', vehicleROI, 'Sensitivity', laneSensitivity); figure imshow(birdsEyeViewBW)
Определение местоположения отдельных маркеров маршрута происходит в координатах автомобиля, которые привязываются на датчике камеры. Этот пример использует параболическую модель контура маршрута, ax^2 + основной обмен + c, чтобы представлять маркеры маршрута. Другие представления, такие как полином третьей степени или сплайны, возможны. Преобразование в координаты автомобиля необходимо, в противном случае искривление маркера маршрута не может быть правильно представлено параболой, в то время как это затронуто перспективным искажением.
Модель маршрута содержит для маркеров маршрута вдоль пути автомобиля. Маркеры маршрута, идущие через путь или дорожные знаки, подрисовали асфальт, отклоняются.
% Obtain lane candidate points in vehicle coordinates
[imageX, imageY] = find(birdsEyeViewBW);
xyBoundaryPoints = imageToVehicle(birdsEyeConfig, [imageY, imageX]);
Поскольку сегментированные точки содержат много выбросов, которые не являются частью фактических маркеров маршрута, используют устойчивый алгоритм подбора кривой на основе согласия случайной выборки (RANSAC).
Возвратите контуры и их параметры параболы (a, b, c) в массиве объектов parabolicLaneBoundary
, boundaries
.
maxLanes = 2; % look for maximum of two lane markers boundaryWidth = 3*approxLaneMarkerWidthVehicle; % expand boundary width [boundaries, boundaryPoints] = findParabolicLaneBoundaries(xyBoundaryPoints,boundaryWidth, ... 'MaxNumBoundaries', maxLanes, 'validateBoundaryFcn', @validateBoundaryFcn);
Заметьте, что findParabolicLaneBoundaries
берет указатель на функцию, validateBoundaryFcn
. Эта функция, взятая в качестве примера, перечислена в конце этого примера. Используя этот дополнительный вход позволяет вам отклонить некоторые кривые на основе значений a, b, c параметры. Это может также использоваться, чтобы использовать в своих интересах временную информацию по серии кадров путем ограничения будущего a, b, c значения на основе предыдущих кадров видео.
Некоторые кривые, найденные на предыдущем шаге, могут все еще быть недопустимыми. Например, когда кривая, вписываются в маркеры перехода. Используйте дополнительную эвристику, чтобы отклонить много таких кривых.
% Establish criteria for rejecting boundaries based on their length maxPossibleXLength = diff(vehicleROI(1:2)); minXLength = maxPossibleXLength * 0.60; % establish a threshold % Reject short boundaries isOfMinLength = arrayfun(@(b)diff(b.XExtent) > minXLength, boundaries); boundaries = boundaries(isOfMinLength);
Удалите дополнительные контуры на основе метрики силы, вычисленной функцией findParabolicLaneBoundaries
. Установите порог силы маршрута на основе ROI и размера изображения.
% To compute the maximum strength, assume all image pixels within the ROI % are lane candidate points birdsImageROI = vehicleToImageROI(birdsEyeConfig, vehicleROI); [laneImageX,laneImageY] = meshgrid(birdsImageROI(1):birdsImageROI(2),birdsImageROI(3):birdsImageROI(4)); % Convert the image points to vehicle points vehiclePoints = imageToVehicle(birdsEyeConfig,[laneImageX(:),laneImageY(:)]); % Find the maximum number of unique x-axis locations possible for any lane % boundary maxPointsInOneLane = numel(unique(vehiclePoints(:,1))); % Set the maximum length of a lane boundary to the ROI length maxLaneLength = diff(vehicleROI(1:2)); % Compute the maximum possible lane strength for this image size/ROI size % specification maxStrength = maxPointsInOneLane/maxLaneLength; % Reject weak boundaries isStrong = [boundaries.Strength] > 0.4*maxStrength; boundaries = boundaries(isStrong);
Эвристика, чтобы классифицировать тип маркера маршрута как твердый/пунктирный включена в функцию помощника, перечисленную в нижней части этого примера. Знание типа маркера маршрута очень важно для регулирования автомобиля автоматически. Например, пересечение твердого маркера запрещается.
boundaries = classifyLaneTypes(boundaries, boundaryPoints); % Locate two ego lanes if they are present xOffset = 0; % 0 meters from the sensor distanceToBoundaries = boundaries.computeBoundaryModel(xOffset); % Find candidate ego boundaries leftEgoBoundaryIndex = []; rightEgoBoundaryIndex = []; minLDistance = min(distanceToBoundaries(distanceToBoundaries>0)); minRDistance = max(distanceToBoundaries(distanceToBoundaries<=0)); if ~isempty(minLDistance) leftEgoBoundaryIndex = distanceToBoundaries == minLDistance; end if ~isempty(minRDistance) rightEgoBoundaryIndex = distanceToBoundaries == minRDistance; end leftEgoBoundary = boundaries(leftEgoBoundaryIndex); rightEgoBoundary = boundaries(rightEgoBoundaryIndex);
Покажите обнаруженные маркеры маршрута в изображении вида с высоты птичьего полета и в регулярном представлении.
xVehiclePoints = bottomOffset:distAheadOfSensor; birdsEyeWithEgoLane = insertLaneBoundary(birdsEyeImage, leftEgoBoundary , birdsEyeConfig, xVehiclePoints, 'Color','Red'); birdsEyeWithEgoLane = insertLaneBoundary(birdsEyeWithEgoLane, rightEgoBoundary, birdsEyeConfig, xVehiclePoints, 'Color','Green'); frameWithEgoLane = insertLaneBoundary(frame, leftEgoBoundary, sensor, xVehiclePoints, 'Color','Red'); frameWithEgoLane = insertLaneBoundary(frameWithEgoLane, rightEgoBoundary, sensor, xVehiclePoints, 'Color','Green'); figure subplot('Position', [0, 0, 0.5, 1.0]) % [left, bottom, width, height] in normalized units imshow(birdsEyeWithEgoLane) subplot('Position', [0.5, 0, 0.5, 1.0]) imshow(frameWithEgoLane)
Обнаружение и отслеживание автомобилей очень важны в системах автономного экстренного торможения (AEB) и переднем предупреждении столкновения (FCW).
Загрузите детектор совокупных функций канала (ACF), который предварительно обучен обнаружить переднюю сторону и заднюю часть автомобилей. Детектор как это может обработать сценарии, где выдавание предупреждения столкновения важно. Это не достаточно, например, для обнаружения автомобиля, перемещающегося через дорогу перед автомобилем, оборудованным датчиком.
detector = vehicleDetectorACF();
% Width of a common vehicle is between 1.5 to 2.5 meters
vehicleWidth = [1.5, 2.5];
Используйте функцию configureDetectorMonoCamera
, чтобы специализировать типичный детектор ACF, чтобы учесть геометрию типичного автомобильного приложения. Путем передачи в этой настройке камеры этот новый детектор ищет только автомобили вдоль поверхности дороги, потому что нет никакого смысла ищущего автомобили высоко над пределом. Это экономит вычислительное время и сокращает количество ложных положительных сторон.
monoDetector = configureDetectorMonoCamera(detector, sensor, vehicleWidth); [bboxes, scores] = detect(monoDetector, frame);
Поскольку этот пример показывает, как обработать только один кадр в демонстрационных целях, вы не можете применить отслеживание сверх необработанных обнаружений. Сложение отслеживания делает результаты возвращающихся местоположений автомобиля более устойчивыми, потому что, даже когда автомобиль частично закрывается, средство отслеживания продолжает возвращать местоположение автомобиля. Для получения дополнительной информации смотрите Дорожку Несколько Автомобилей Используя пример Камеры.
Затем, преобразуйте обнаружения автомобиля, чтобы транспортировать координаты. Функция computeVehicleLocations
, включенная в конце этого примера, вычисляет, местоположение автомобиля в автомобиле координирует, учитывая ограничительную рамку, возвращенную алгоритмом обнаружения в координатах изображений. Это возвращает центральное местоположение нижней части ограничительной рамки в координатах автомобиля. Поскольку мы используем монокулярный датчик камеры и простую homography, только расстояния вдоль поверхности дороги могут быть вычислены точно. Вычисление произвольного местоположения на 3-D пробеле требует использования стереофотоаппарата или другого датчика, способного к триангуляции.
locations = computeVehicleLocations(bboxes, sensor);
% Overlay the detections on the video frame
imgOut = insertVehicleDetections(frame, locations, bboxes);
figure;
imshow(imgOut);
Теперь, когда у вас есть идея о внутренних работах отдельных шагов, давайте соединим их и применим их к видео последовательности, где мы можем также использовать в своих интересах временную информацию.
Перемотайте видео к началу, и затем обработайте видео. Код ниже сокращен, потому что все основные параметры были заданы на предыдущих шагах. Здесь, параметры используются без дальнейшего объяснения.
videoReader.CurrentTime = 0; isPlayerOpen = true; snapshot = []; while hasFrame(videoReader) && isPlayerOpen % Grab a frame of video frame = readFrame(videoReader); % Compute birdsEyeView image birdsEyeImage = transformImage(birdsEyeConfig, frame); birdsEyeImage = rgb2gray(birdsEyeImage); % Detect lane boundary features birdsEyeViewBW = segmentLaneMarkerRidge(birdsEyeImage, birdsEyeConfig, ... approxLaneMarkerWidthVehicle, 'ROI', vehicleROI, ... 'Sensitivity', laneSensitivity); % Obtain lane candidate points in vehicle coordinates [imageX, imageY] = find(birdsEyeViewBW); xyBoundaryPoints = imageToVehicle(birdsEyeConfig, [imageY, imageX]); % Find lane boundary candidates [boundaries, boundaryPoints] = findParabolicLaneBoundaries(xyBoundaryPoints,boundaryWidth, ... 'MaxNumBoundaries', maxLanes, 'validateBoundaryFcn', @validateBoundaryFcn); % Reject boundaries based on their length and strength isOfMinLength = arrayfun(@(b)diff(b.XExtent) > minXLength, boundaries); boundaries = boundaries(isOfMinLength); isStrong = [boundaries.Strength] > 0.2*maxStrength; boundaries = boundaries(isStrong); % Classify lane marker type boundaries = classifyLaneTypes(boundaries, boundaryPoints); % Find ego lanes xOffset = 0; % 0 meters from the sensor distanceToBoundaries = boundaries.computeBoundaryModel(xOffset); % Find candidate ego boundaries leftEgoBoundaryIndex = []; rightEgoBoundaryIndex = []; minLDistance = min(distanceToBoundaries(distanceToBoundaries>0)); minRDistance = max(distanceToBoundaries(distanceToBoundaries<=0)); if ~isempty(minLDistance) leftEgoBoundaryIndex = distanceToBoundaries == minLDistance; end if ~isempty(minRDistance) rightEgoBoundaryIndex = distanceToBoundaries == minRDistance; end leftEgoBoundary = boundaries(leftEgoBoundaryIndex); rightEgoBoundary = boundaries(rightEgoBoundaryIndex); % Detect vehicles [bboxes, scores] = detect(monoDetector, frame); locations = computeVehicleLocations(bboxes, sensor); % Visualize sensor outputs and intermediate results. Pack the core % sensor outputs into a struct. sensorOut.leftEgoBoundary = leftEgoBoundary; sensorOut.rightEgoBoundary = rightEgoBoundary; sensorOut.vehicleLocations = locations; sensorOut.xVehiclePoints = bottomOffset:distAheadOfSensor; sensorOut.vehicleBoxes = bboxes; % Pack additional visualization data, including intermediate results intOut.birdsEyeImage = birdsEyeImage; intOut.birdsEyeConfig = birdsEyeConfig; intOut.vehicleScores = scores; intOut.vehicleROI = vehicleROI; intOut.birdsEyeBW = birdsEyeViewBW; closePlayers = ~hasFrame(videoReader); isPlayerOpen = visualizeSensorResults(frame, sensor, sensorOut, ... intOut, closePlayers); timeStamp = 7.5333; % take snapshot for publishing at timeStamp seconds if abs(videoReader.CurrentTime - timeStamp) < 0.01 snapshot = takeSnapshot(frame, sensor, sensorOut); end end
Отобразите кадр видео. Снимок состояния взят в секунды timeStamp
.
if ~isempty(snapshot) figure imshow(snapshot) end
Класс helperMonoSensor
собирает настройку и все необходимые шаги, чтобы моделировать монокулярный датчик камеры в полный пакет, который может быть применен к любому видео. Поскольку большинство параметров, используемых проектом датчика, основано на мировых модулях, проект устойчив к изменениям при закрытых дверях параметры, включая размер изображения. Обратите внимание на то, что код в классе helperMonoSensor
отличается от цикла в предыдущем разделе, который использовался, чтобы проиллюстрировать фундаментальные понятия.
Помимо обеспечения нового видео, необходимо предоставить настройку камеры, соответствующую тому видео. Процесс показывают здесь. Попробуйте его на своих собственных видео.
% Sensor configuration focalLength = [309.4362, 344.2161]; principalPoint = [318.9034, 257.5352]; imageSize = [480, 640]; height = 2.1798; % mounting height in meters from the ground pitch = 14; % pitch of the camera in degrees camIntrinsics = cameraIntrinsics(focalLength, principalPoint, imageSize); sensor = monoCamera(camIntrinsics, height, 'Pitch', pitch); videoReader = VideoReader('caltech_washington1.avi');
Создайте объект helperMonoSensor
и примените его к видео.
monoSensor = helperMonoSensor(sensor); monoSensor.LaneXExtentThreshold = 0.5; % To remove false detections from shadows in this video, we only return % vehicle detections with higher scores. monoSensor.VehicleDetectionThreshold = 20; isPlayerOpen = true; snapshot = []; while hasFrame(videoReader) && isPlayerOpen frame = readFrame(videoReader); % get a frame sensorOut = processFrame(monoSensor, frame); closePlayers = ~hasFrame(videoReader); isPlayerOpen = displaySensorOutputs(monoSensor, frame, sensorOut, closePlayers); timeStamp = 11.1333; % take snapshot for publishing at timeStamp seconds if abs(videoReader.CurrentTime - timeStamp) < 0.01 snapshot = takeSnapshot(frame, sensor, sensorOut); end end
Отобразите кадр видео. Снимок состояния взят в секунды timeStamp
.
if ~isempty(snapshot) figure imshow(snapshot) end
отображения visualizeSensorResults базовая информация и промежуточное звено следуют из монокулярной симуляции датчика камеры.
function isPlayerOpen = visualizeSensorResults(frame, sensor, sensorOut,... intOut, closePlayers) % Unpack the main inputs leftEgoBoundary = sensorOut.leftEgoBoundary; rightEgoBoundary = sensorOut.rightEgoBoundary; locations = sensorOut.vehicleLocations; xVehiclePoints = sensorOut.xVehiclePoints; bboxes = sensorOut.vehicleBoxes; % Unpack additional intermediate data birdsEyeViewImage = intOut.birdsEyeImage; birdsEyeConfig = intOut.birdsEyeConfig; vehicleROI = intOut.vehicleROI; birdsEyeViewBW = intOut.birdsEyeBW; % Visualize left and right ego-lane boundaries in bird's-eye view birdsEyeWithOverlays = insertLaneBoundary(birdsEyeViewImage, leftEgoBoundary , birdsEyeConfig, xVehiclePoints, 'Color','Red'); birdsEyeWithOverlays = insertLaneBoundary(birdsEyeWithOverlays, rightEgoBoundary, birdsEyeConfig, xVehiclePoints, 'Color','Green'); % Visualize ego-lane boundaries in camera view frameWithOverlays = insertLaneBoundary(frame, leftEgoBoundary, sensor, xVehiclePoints, 'Color','Red'); frameWithOverlays = insertLaneBoundary(frameWithOverlays, rightEgoBoundary, sensor, xVehiclePoints, 'Color','Green'); frameWithOverlays = insertVehicleDetections(frameWithOverlays, locations, bboxes); imageROI = vehicleToImageROI(birdsEyeConfig, vehicleROI); ROI = [imageROI(1) imageROI(3) imageROI(2)-imageROI(1) imageROI(4)-imageROI(3)]; % Highlight candidate lane points that include outliers birdsEyeViewImage = insertShape(birdsEyeViewImage, 'rectangle', ROI); % show detection ROI birdsEyeViewImage = imoverlay(birdsEyeViewImage, birdsEyeViewBW, 'blue'); % Display the results frames = {frameWithOverlays, birdsEyeViewImage, birdsEyeWithOverlays}; persistent players; if isempty(players) frameNames = {'Lane marker and vehicle detections', 'Raw segmentation', 'Lane marker detections'}; players = helperVideoPlayerSet(frames, frameNames); end update(players, frames); % Terminate the loop when the first player is closed isPlayerOpen = isOpen(players, 1); if (~isPlayerOpen || closePlayers) % close down the other players clear players; end end
computeVehicleLocations вычисляет местоположение автомобиля в координатах автомобиля, учитывая ограничительную рамку, возвращенную алгоритмом обнаружения в координатах изображений. Это возвращает центральное местоположение нижней части ограничительной рамки в координатах автомобиля. Поскольку монокулярный датчик камеры и простая homography используются, только расстояния вдоль поверхности дороги могут быть вычислены. Вычисление произвольного местоположения на 3-D пробеле требует использования стереофотоаппарата или другого датчика, способного к триангуляции.
function locations = computeVehicleLocations(bboxes, sensor) locations = zeros(size(bboxes,1),2); for i = 1:size(bboxes, 1) bbox = bboxes(i, :); % Get [x,y] location of the center of the lower portion of the % detection bounding box in meters. bbox is [x, y, width, height] in % image coordinates, where [x,y] represents upper-left corner. yBottom = bbox(2) + bbox(4) - 1; xCenter = bbox(1) + (bbox(3)-1)/2; % approximate center locations(i,:) = imageToVehicle(sensor, [xCenter, yBottom]); end end
insertVehicleDetections вставляет ограничительные рамки и отображения [x, y] местоположения, соответствующие возвращенным обнаружениям автомобиля.
function imgOut = insertVehicleDetections(imgIn, locations, bboxes) imgOut = imgIn; for i = 1:size(locations, 1) location = locations(i, :); bbox = bboxes(i, :); label = sprintf('X=%0.2f, Y=%0.2f', location(1), location(2)); imgOut = insertObjectAnnotation(imgOut, ... 'rectangle', bbox, label, 'Color','g'); end end
vehicleToImageROI преобразовывает ROI в координатах автомобиля, чтобы отобразить координаты в изображении вида с высоты птичьего полета.
function imageROI = vehicleToImageROI(birdsEyeConfig, vehicleROI) vehicleROI = double(vehicleROI); loc2 = abs(vehicleToImage(birdsEyeConfig, [vehicleROI(2) vehicleROI(4)])); loc1 = abs(vehicleToImage(birdsEyeConfig, [vehicleROI(1) vehicleROI(4)])); loc4 = vehicleToImage(birdsEyeConfig, [vehicleROI(1) vehicleROI(4)]); loc3 = vehicleToImage(birdsEyeConfig, [vehicleROI(1) vehicleROI(3)]); [minRoiX, maxRoiX, minRoiY, maxRoiY] = deal(loc4(1), loc3(1), loc2(2), loc1(2)); imageROI = round([minRoiX, maxRoiX, minRoiY, maxRoiY]); end
validateBoundaryFcn отклоняет некоторые пограничные кривые маршрута, вычисленные с помощью алгоритма RANSAC.
function isGood = validateBoundaryFcn(params) if ~isempty(params) a = params(1); % Reject any curve with a small 'a' coefficient, which makes it highly % curved. isGood = abs(a) < 0.003; % a from ax^2+bx+c else isGood = false; end end
classifyLaneTypes определяет типы маркера маршрута как solid
, dashed
, и т.д.
function boundaries = classifyLaneTypes(boundaries, boundaryPoints) for bInd = 1 : numel(boundaries) vehiclePoints = boundaryPoints{bInd}; % Sort by x vehiclePoints = sortrows(vehiclePoints, 1); xVehicle = vehiclePoints(:,1); xVehicleUnique = unique(xVehicle); % Dashed vs solid xdiff = diff(xVehicleUnique); % Sufficiently large threshold to remove spaces between points of a % solid line, but not large enough to remove spaces between dashes xdifft = mean(xdiff) + 3*std(xdiff); largeGaps = xdiff(xdiff > xdifft); % Safe default boundaries(bInd).BoundaryType= LaneBoundaryType.Solid; if largeGaps>2 % Ideally, these gaps should be consistent, but you cannot rely % on that unless you know that the ROI extent includes at least 3 dashes. boundaries(bInd).BoundaryType = LaneBoundaryType.Dashed; end end end
takeSnapshot получает вывод для отчета публикующего HTML.
function I = takeSnapshot(frame, sensor, sensorOut) % Unpack the inputs leftEgoBoundary = sensorOut.leftEgoBoundary; rightEgoBoundary = sensorOut.rightEgoBoundary; locations = sensorOut.vehicleLocations; xVehiclePoints = sensorOut.xVehiclePoints; bboxes = sensorOut.vehicleBoxes; frameWithOverlays = insertLaneBoundary(frame, leftEgoBoundary, sensor, xVehiclePoints, 'Color','Red'); frameWithOverlays = insertLaneBoundary(frameWithOverlays, rightEgoBoundary, sensor, xVehiclePoints, 'Color','Green'); frameWithOverlays = insertVehicleDetections(frameWithOverlays, locations, bboxes); I = frameWithOverlays; end
configureDetectorMonoCamera
| estimateMonoCameraParameters
| extrinsics
| findParabolicLaneBoundaries
| segmentLaneMarkerRidge