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

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

Примечание: преобразование между системами координат принимает плоскую дорогу. Это основано на установлении матрицы 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 = 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

% Find short boundaries
if( numel(boundaries) > 0 )
    isOfMinLength = false(1, numel(boundaries));
    for i = 1 : numel(boundaries)
        if(diff(boundaries(i).XExtent) > minXLength)
            isOfMinLength(i) = true;
        end
    end
else
    isOfMinLength = false;
end

Удалите дополнительные контуры на основе метрики силы, вычисленной 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(single((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 short and weak boundaries
idx = 0;
strongBoundaries = parabolicLaneBoundary(zeros(nnz(isOfMinLength), 3));
for i = 1 : size(isOfMinLength,2)
    if( isOfMinLength(i) == 1 )
        if( boundaries(i).Strength > 0.4*maxStrength )
            idx = idx + 1;
            strongBoundaries(idx) = boundaries(i);
        end
    end
end

Эвристика, чтобы классифицировать тип маркера маршрута как твердый/пунктирный включена в функцию помощника, перечисленную в нижней части этого примера. Знание типа маркера маршрута очень важно для регулирования транспортного средства автоматически. Например, пересечение твердого маркера запрещается.

% Classify lane marker type when boundaryPoints are not empty
if isempty(boundaryPoints)
    strongBoundaries = repmat(strongBoundaries,1,2);
    strongBoundaries(1) = parabolicLaneBoundary(zeros(1,3));
    strongBoundaries(2) = parabolicLaneBoundary(zeros(1,3));
else
    strongBoundaries = classifyLaneTypes(strongBoundaries, boundaryPoints);
end

distancesToBoundaries = coder.nullcopy(ones(size(strongBoundaries,2),1));

% Find ego lanes
xOffset    = 0;   %  0 meters from the sensor
for i = 1 : size(strongBoundaries, 2)
    distancesToBoundaries(i) = strongBoundaries(i).computeBoundaryModel(xOffset);
end

% Find candidate ego boundaries
distancesToLeftBoundary = distancesToBoundaries>0;
if (numel(distancesToBoundaries(distancesToLeftBoundary)))
    minLeftDistance = min(distancesToBoundaries(distancesToLeftBoundary));
else
    minLeftDistance = 0;
end

distancesToRightBoundary = (distancesToBoundaries <= 0);
if( numel(distancesToBoundaries(distancesToRightBoundary)))
    minRightDistance = max(distancesToBoundaries(distancesToRightBoundary));
else
    minRightDistance = 0;
end

% Find left ego boundary
if (minLeftDistance ~= 0)
    leftEgoBoundaryIndex  = distancesToBoundaries == minLeftDistance;
    leftEgoBoundary = parabolicLaneBoundary(zeros(nnz(leftEgoBoundaryIndex), 3));
    idx = 0;
    for i = 1 : size(leftEgoBoundaryIndex, 1)
        if( leftEgoBoundaryIndex(i) == 1)
            idx = idx + 1;
            leftEgoBoundary(idx) = strongBoundaries(i);
        end
    end
else
    leftEgoBoundary = parabolicLaneBoundary.empty();
end

% Find right ego boundary
if (minRightDistance ~= 0)
    rightEgoBoundaryIndex = distancesToBoundaries == minRightDistance;
    rightEgoBoundary = parabolicLaneBoundary(zeros(nnz(rightEgoBoundaryIndex), 3));
    idx = 0;
    for i = 1 : size(rightEgoBoundaryIndex, 1)
        if( rightEgoBoundaryIndex(i) == 1)
            idx = idx + 1;
            rightEgoBoundary(idx) = strongBoundaries(i);
        end
    end
else
    rightEgoBoundary = parabolicLaneBoundary.empty();
end

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

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

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    
    % Find short boundaries
    if( numel(boundaries) > 0 )
        isOfMinLength = false(1, numel(boundaries));
        for i = 1 : numel(boundaries)
            if(diff(boundaries(i).XExtent) > minXLength)
                isOfMinLength(i) = true;
            end
        end
    else
        isOfMinLength = false;
    end

    % Reject short and weak boundaries
    idx = 0;
    strongBoundaries = parabolicLaneBoundary(zeros(nnz(isOfMinLength), 3));
    for i = 1 : size(isOfMinLength,2)
        if( isOfMinLength(i) == 1 )
            if( boundaries(i).Strength > 0.2*maxStrength )
                idx = idx + 1;
                strongBoundaries(idx) = boundaries(i);
            end
        end
    end

    boundaries    = boundaries(isOfMinLength);
    isStrong      = [boundaries.Strength] > 0.2*maxStrength;
    boundaries    = boundaries(isStrong);

    % Classify lane marker type when boundaryPoints are not empty
    if isempty(boundaryPoints)
        strongBoundaries = repmat(strongBoundaries,1,2);
        strongBoundaries(1) = parabolicLaneBoundary(zeros(1,3));
        strongBoundaries(2) = parabolicLaneBoundary(zeros(1,3));
    else
        strongBoundaries = classifyLaneTypes(strongBoundaries, boundaryPoints);
    end     
        
    % Find ego lanes
    xOffset    = 0;   %  0 meters from the sensor
    distancesToBoundaries = coder.nullcopy(ones(size(strongBoundaries,2),1));

    for i = 1 : size(strongBoundaries, 2)
        distancesToBoundaries(i) = strongBoundaries(i).computeBoundaryModel(xOffset);
    end
    % Find candidate ego boundaries
    distancesToLeftBoundary = distancesToBoundaries>0;
    if (numel(distancesToBoundaries(distancesToLeftBoundary)))
        minLeftDistance = min(distancesToBoundaries(distancesToLeftBoundary));
    else
        minLeftDistance = 0;
    end

    distancesToRightBoundary = (distancesToBoundaries <= 0);
    if( numel(distancesToBoundaries(distancesToRightBoundary)))
        minRightDistance = max(distancesToBoundaries(distancesToRightBoundary));
    else
        minRightDistance = 0;
    end

    % Find left ego boundary
    if (minLeftDistance ~= 0)
        leftEgoBoundaryIndex  = distancesToBoundaries == minLeftDistance;
        leftEgoBoundary = parabolicLaneBoundary(zeros(nnz(leftEgoBoundaryIndex), 3));
        idx = 0;
        for i = 1 : size(leftEgoBoundaryIndex, 1)
            if( leftEgoBoundaryIndex(i) == 1)
                idx = idx + 1;
                leftEgoBoundary(idx) = strongBoundaries(i);
            end
        end
    else
        leftEgoBoundary = parabolicLaneBoundary.empty();
    end
    % Find right ego boundary
    if (minRightDistance ~= 0)
        rightEgoBoundaryIndex = distancesToBoundaries == minRightDistance;
        rightEgoBoundary = parabolicLaneBoundary(zeros(nnz(rightEgoBoundaryIndex), 3));
        idx = 0;
        for i = 1 : size(rightEgoBoundaryIndex, 1)
            if( rightEgoBoundaryIndex(i) == 1)
                idx = idx + 1;
                rightEgoBoundary(idx) = strongBoundaries(i);
            end
        end
    else
        rightEgoBoundary = parabolicLaneBoundary.empty();
    end

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

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 : size(boundaries,2)

    vehiclePoints = boundaryPoints{bInd};
    % Sort by x
    vehiclePoints = sortrows(vehiclePoints, 1);

    xVehicle = vehiclePoints(:,1);
    xVehicleUnique = unique(xVehicle);

    % Dashed vs solid
    xdiff  = diff(xVehicleUnique);
    % Set a threshold to remove gaps in solid line but not the spaces from
    % dashed lines.
    xdiffThreshold = mean(xdiff) + 3*std(xdiff);
    largeGaps = xdiff(xdiff > xdiffThreshold);

    % Safe default
    boundary = boundaries(bInd);           % changed according to set/get methods
    boundary.BoundaryType= LaneBoundaryType.Solid;

    if largeGaps>1
        % Ideally, these gaps should be consistent, but you cannot rely
        % on that unless you know that the ROI extent includes at least 3 dashes.
        boundary.BoundaryType= LaneBoundaryType.Dashed;
    end
    boundaries(bInd) = boundary;
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

Смотрите также

Приложения

Функции

Объекты

Похожие темы