Визуальное восприятие с использованием монокулярной камеры

Этот пример показывает, как создать монокулярную симуляцию датчика камеры, способную к контурам маршрута и обнаружениям транспортного средства. Датчик сообщит об этих обнаружениях в системе координат транспортного средства. В этом примере вы узнаете о системе координат, используемой 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);

Затем определите ориентацию камеры относительно шасси транспортного средства. Эта информация будет использоваться для создания внешних данных камеры, которые определяют положение 3-D системы координат камеры относительно системы координат транспортного средства.

height = 2.1798;    % mounting height in meters from the ground
pitch  = 14;        % pitch of the camera in degrees

Вышеуказанные величины могут быть выведены из матриц вращения и перемещения, возвращенных extrinsics функция. Тангаж задает наклон камеры от горизонтального положения. Для камеры, используемой в этом примере, крен и рыскание датчика равны нулю. Всёсь строение, определяющая внутреннюю и внешнюю составляющие, хранится в monoCamera объект.

sensor = monoCamera(camIntrinsics, height, 'Pitch', pitch);

Обратите внимание, что monoCamera объект настраивает очень специфическую систему координат транспортного средства, где ось X указывает вперед от транспортного средства, ось Y указывает налево от транспортного средства, а ось Z указывает вверх от земли.

По умолчанию источник системы координат находится на земле, прямо под центром камеры, заданным фокальной точкой камеры. Перемещение источника осуществляется с помощью SensorLocation свойство monoCamera объект. Кроме того, monoCamera обеспечивает imageToVehicle и vehicleToImage способы преобразования между изображением и системами координат транспортного средства.

Примечание. Преобразование между системами координат принимает плоскую дорогу. Он основан на установлении гомографической матрицы, которая сопоставляет местоположения на плоскости формирования изображения с местоположениями на поверхности дороги. Нелетающие дороги вносят ошибки в расчеты расстояния, особенно в местах, которые находятся далеко от транспортного средства.

Загрузка системы координат видео

Перед обработкой всего видео обработайте один видеокадр, чтобы проиллюстрировать концепции, участвующие в проекте монокулярного датчика камеры.

Начните, создав 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 функция для отделения пикселей кандидата маркера маршрута от дорожного покрытия. Этот метод был выбран за его простоту и относительную эффективность. Существуют альтернативные методы сегментации, включая семантическую сегментацию (глубокое обучение) и управляемые фильтры. Можно заменить эти методы ниже, чтобы получить бинарную маску, необходимую для следующего этапа.

Большинство входных параметров в функции ниже заданы в мировых единицах измерения, например, ширина маркера маршрута, подаваемая в segmentLaneMarkerRidge. Использование мировых единиц измерения позволяет легко попробовать новые датчики, даже когда изменяется вход размер изображения. Это очень важно для того, чтобы сделать проект более устойчивым и гибким в отношении изменения оборудования камеры и обработки различных стандартов во многих странах.

% Convert to grayscale
birdsEyeImage = im2gray(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 + bx + 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 на основе предыдущих видеокадров.

Определите Контуры Ego Lane

Некоторые кривые, найденные на предыдущем шаге, все еще могут быть недопустимыми. Для примера, когда кривая помещается в маркеры перекрестия. Используйте дополнительную эвристику, чтобы отклонить многие такие кривые.

% 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 функция. Установите порог прочности маршрута на основе информация только для чтения и размера изображения.

% 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)

Определение местоположения транспортных средств в координатах транспортных средств

Обнаружение и отслеживание транспортных средств является критическим в системах предупреждения столкновения (FCW) и автономного экстренного торможения (AEB).

Загрузите детектор совокупных функций канала (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);

Поскольку в этом примере показано, как обработать только одну систему координат в демонстрационных целях, вы не можете применить отслеживание поверх необработанных обнаружений. Сложение отслеживания делает результаты возврата местоположения транспортного средства более устойчивыми, потому что, даже когда транспортное средство частично закрыта, трекер продолжает возвращать местоположение транспортного средства. Для получения дополнительной информации см. пример «Отслеживать несколько транспортных средств с помощью камеры».

Затем преобразуйте обнаружения транспортного средства в координаты автомобиля. The computeVehicleLocations функция, включенная в конец этого примера, вычисляет местоположение транспортного средства в координатах транспортного средства, заданных ограничивающий прямоугольник, возвращенный алгоритмом обнаружения в координатах изображения. Оно возвращает центральное положение дна ограничивающего прямоугольника в координатах транспортного средства. Поскольку мы используем монокулярный датчик камеры и простую гомографию, точно можно вычислить только расстояния вдоль поверхности дороги. Расчет произвольного местоположения в трехмерном простртребуеттребует использования стереофотоаппарата или другого датчика, способного к триангуляции.

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 = im2gray(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

Попробуйте проект датчика на другом видео

The 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 вычисляет местоположение транспортного средства в координатах транспортного средства, учитывая ограничивающий прямоугольник, возвращенный алгоритмом обнаружения в координатах изображения. Оно возвращает центральное положение дна ограничивающего прямоугольника в координатах транспортного средства. Поскольку используются монокулярный датчик камеры и простая гомография, можно вычислить только расстояния вдоль поверхности дороги. Расчет произвольного местоположения в трехмерном простртребуеттребует использования стереофотоаппарата или другого датчика, способного к триангуляции.

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 преобразует информация только для чтения в координатах транспортного средства в координаты изображения в изображении вида птичьего глаза.

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

См. также

Приложения

Функции

Объекты

Похожие темы