exponenta event banner

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

В этом примере показано, как создать монокулярный датчик камеры, способный обнаруживать границу полосы движения и транспортное средство. Датчик сообщит об этих обнаружениях в системе координат транспортного средства. В этом примере вы узнаете о системе координат, используемой в Automated Driving Toolbox™, и методах компьютерного зрения, используемых при проектировании образца датчика монокулярной камеры.

Обзор

Транспортные средства, которые содержат функции ADAS или спроектированы так, чтобы быть полностью автономными, полагаются на множество датчиков. Эти датчики могут включать в себя гидролокатор, радар, лидар и камеры. Этот пример иллюстрирует некоторые концепции, связанные с конструкцией системы монокулярной камеры. Такой датчик может выполнять множество задач, в том числе:

  • Обнаружение границ полосы движения

  • Обнаружение транспортных средств, людей и других объектов

  • Оценка расстояния от эго-транспортного средства до препятствий

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

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

Определение конфигурации камеры

Знание собственных и внешних параметров калибровки камеры имеет решающее значение для точного преобразования между координатами пикселя и транспортного средства.

Начните с определения собственных параметров камеры. Приведенные ниже параметры были определены ранее с использованием процедуры калибровки камеры, в которой использовалась схема калибровки шашки. Для их получения можно использовать приложение «Калибратор камеры».

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 на основе предыдущих видеокадров.

Определение границ переулка Эго

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

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

Размещение транспортных средств в координатах транспортного средства

Обнаружение и отслеживание транспортных средств имеет решающее значение для систем предупреждения о лобовом столкновении (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);

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

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

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

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

Вспомогательные функции

визуализация SensorResults отображает основную информацию и промежуточные результаты моделирования датчика монокулярной камеры.

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

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

insert VehicleDetections вставляет ограничивающие рамки и отображает местоположения [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

validateFcn отклоняет некоторые граничные кривые полосы движения, вычисленные с использованием алгоритма 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

classiedLaneTypes определяет типы маркеров полос движения как 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

См. также

Приложения

Функции

Объекты

Связанные темы