Аннотирование видео с использованием обнаружений в координатах транспортного средства

Настройте и используйте monoCamera объект для отображения информации, представленной в координатах транспортного средства, на видеодисплее.

Обзор

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

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

  • Информация о маршруте: 20 раз в секунду

  • Сообщенные объекты зрения: 10 раз в секунду

  • Частота видеокадров: 20 системы координат в секунду

Отображение Системы координат с аннотациями к видео

Выбранная система координат соответствует 5,9 секундам в видеоклипе, когда есть несколько объектов, которые нужно показать на видео.

% Set up video reader and player
videoFile      = '01_city_c2s_fcw_10s.mp4';
videoReader = VideoReader(videoFile);
videoPlayer = vision.DeployableVideoPlayer;

% Jump to the desired frame
time = 5.9;
videoReader.CurrentTime = time;
frameWithoutAnnotations = readFrame(videoReader);

imshow(frameWithoutAnnotations); 
title('Original Video Frame')

Figure contains an axes. The axes with title Original Video Frame contains an object of type image.

Получите соответствующие записанные данные.

recordingFile  = '01_city_c2s_fcw_10s_sensor.mat';
[visionObjects, laneReports, timeStep, numSteps] = readDetectionsFile(recordingFile);
currentStep = round(time / timeStep) + 1;
videoDetections = processDetections(visionObjects(currentStep));
laneBoundaries = processLanes(laneReports(currentStep));

% Set up the monoCamera object for on-video display
sensor = setupMonoCamera(videoReader);

frameWithAnnotations = updateDisplay(frameWithoutAnnotations, sensor, videoDetections, laneBoundaries);

imshow(frameWithAnnotations); 
title('Annotated Video Frame')

Figure contains an axes. The axes with title Annotated Video Frame contains an object of type image.

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

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

% Reset the time back to zero
currentStep = 0;                % Reset the recorded data timestep
videoReader.CurrentTime = 0;    % Reset the video reader time
while currentStep < numSteps && hasFrame(videoReader)
    % Update scenario counters
    currentStep = currentStep + 1;
    
    % Get the current time
    tic
    
    % Prepare the detections to the tracker
    videoDetections = processDetections(visionObjects(currentStep), videoDetections);

    % Process lanes
    laneBoundaries = processLanes(laneReports(currentStep));
    
    % Update video frame with annotations from the reported objects
    frameWithoutAnnotations = readFrame(videoReader);
    frameWithAnnotations = updateDisplay(frameWithoutAnnotations, sensor, videoDetections, laneBoundaries);
    
    % The recorded data was obtained at a rate of 20 frames per second.
    % Pause for 50 milliseconds for a more realistic display rate. If you
    % process data and form tracks in this loop, you do not need this
    % pause.
    pause(0.05 - toc);
    
    % Display annotated frame
    videoPlayer(frameWithAnnotations);    
end

Создайте камеру Mono для видео- Отображения

The setupMonoCamera функция возвращает monoCamera объект, который используется для преобразования позиций в координатах транспортного средства в координаты изображения.

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

Начните, определив собственные параметры камеры. Параметры в этой функции оценивались на основе модели камеры. Чтобы получить параметры для камеры, используйте приложение Camera Calibrator.

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

Далее задайте внешнюю информацию камеры. Внешние камеры относятся к тому, как камера установлена на автомобиле. Монтаж включает следующие свойства:

  • Высота: Высота монтажа над землей, в метрах.

  • Тангаж: Тангаж камеры, в степенях, где позитив наклонен ниже горизонта и к земле. В большинстве случаев камера наклонена немного ниже горизонта.

  • Крен: Крен камеры вокруг ее оси. Для примера, если видео перевернуто, используйте крен = 180.

  • Рыскание: Угол камеры в сторону, где положительный в направлении положительной оси y (налево). Для примера камера, обращенная вперед, имеет угол рыскания 0 степеней, а камера, обращенная назад, - угол рыскания 180 степеней.

function sensor = setupMonoCamera(vidReader)
% Define the camera intrinsics from the video information
focalLength    = [1260 1100];                    % [fx, fy]              % pixels
principalPoint = [360 245];                      % [cx, cy]              % pixels
imageSize = [vidReader.height, vidReader.width]; % [numRows, numColumns] % pixels
intrinsics = cameraIntrinsics(focalLength, principalPoint, imageSize);

% Define the camera mounting (camera extrinsics)
mountingHeight = 1.45;   % height in meters from the ground
mountingPitch  = 1.25;   % pitch of the camera in degrees
mountingRoll   = 0.15;   % roll of the camera in degrees
mountingYaw    = 0;      % yaw of the camera in degrees
sensor = monoCamera(intrinsics, mountingHeight, ...
    'Pitch', mountingPitch, ...
    'Roll',  mountingRoll, ...
    'Yaw',   mountingYaw);
end

Использование объекта Mono Camera для обновления отображения

The updateDisplay функция отображает все аннотации объектов в верхней части видеокадра.

Обновление отображения включает следующие шаги:

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

  2. Использование insertLaneBoundary метод parabolicLaneBoundary объект для вставки аннотаций маршрута.

function frame = updateDisplay(frame, sensor, videoDetections, laneBoundaries)

% Allocate memory for bounding boxes
bboxes = zeros(numel(videoDetections), 4);

% Create the bounding boxes
for i = 1:numel(videoDetections)
    % Use monoCamera sensor to convert the position in vehicle coordinates 
    % to the position in image coordinates. 
    % Notes: 
    %   1. The width of the object is reported and is used to calculate the
    %      size of the bounding box around the object (half width on each
    %      side). The height of the object is not reported. Instead, the 
    %      function uses a height/width ratio of 0.85 for cars and 3 for
    %      pedestrians.
    %   2. The reported location is at the center of the object at ground 
    %      level, i.e., the bottom of the bounding box.
    xyLocation1 = vehicleToImage(sensor, videoDetections(i).positions' + [0,videoDetections(i).widths/2]);
    xyLocation2 = vehicleToImage(sensor, videoDetections(i).positions' - [0,videoDetections(i).widths/2]);
    dx = xyLocation2(1) - xyLocation1(1);
    
    % Define the height/width ratio based on object class
    if strcmp(videoDetections(i).labels, 'Car')
        dy = dx * 0.85;
    elseif  strcmp(videoDetections(i).labels, 'Pedestrian')
        dy = dx * 3;
    else
        dy = dx;
    end
    
    % Estimate the bounding box around the vehicle. Subtract the height of
    % the bounding box to define the top-left corner.
    bboxes(i,:) =[(xyLocation1 - [0, dy]), dx, dy];
end

% Add labels
labels = {videoDetections(:).labels}';

% Add bounding boxes to the frame
if ~isempty(labels)
    frame = insertObjectAnnotation(frame, 'rectangle', bboxes, labels,...
        'Color', 'yellow', 'FontSize', 10, 'TextBoxOpacity', .8, 'LineWidth', 2);
end

% Display the lane boundary on the video frame
xRangeVehicle = [1, 100];
xPtsInVehicle = linspace(xRangeVehicle(1), xRangeVehicle(2), 100)';
frame = insertLaneBoundary(frame, laneBoundaries(1), sensor, xPtsInVehicle, ...
    'Color', 'red');
frame = insertLaneBoundary(frame, laneBoundaries(2), sensor, xPtsInVehicle, ...
    'Color', 'green');
end

Сводные данные

Этот пример показал, как создать monoCamera сенсорный объект и использование его для отображения объектов, описанных в координатах транспортного средства, на видео, захваченном отдельной камерой. Попробуйте использовать собственные записанные данные и видеокамеру. Попробуйте калибровать камеру, чтобы создать monoCamera что позволяет преобразовать от транспортного средства к координатам изображения и наоборот.

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

readDetectionsFile - Считывает записанный файл данных датчика. Записанные данные находятся в одной структуре, которая разделена на четыре struct массивы. Этот пример использует только следующие два массива:

  1. laneReports, а struct массив, который сообщает о контурах маршрута. В нем есть следующие поля: left и right. Каждый элемент массива соответствует другому временному шагу. Оба left и right являются структурами с этими полями: isValid, confidence, boundaryType, offset, headingAngle, и curvature.

  2. visionObjects, массив структур, который сообщает об обнаруженных объектах зрения. В нем есть поля numObjects (integer) и object (struct). Каждый элемент массива соответствует другому временному шагу. object является struct массив, где каждый элемент является отдельным объектом с этими полями: id, classification, position (x;y;z), velocity(vx;vy;vz), size(dx;dy;dz). Примечание: z=vy=vz=dx=dz=0

function [visionObjects, laneReports, timeStep, numSteps] = readDetectionsFile(filename)
A = load(strcat(filename));
visionObjects = A.vision;
laneReports = A.lane;

% Prepare some time variables
timeStep = 0.05;                 % Lane data is provided every 50 milliseconds
numSteps = numel(visionObjects); % Number of recorded timesteps
end

processDetections - считывает зарегистрированные обнаружения зрения. В этом примере извлекаются только следующие свойства:

  1. Положение: Двумерная [x, y] массив в координатах транспортного средства

  2. Ширина: Ширина объекта, как сообщает видеодатчик (Примечание: Датчик не сообщает никаких других размерностей размера объекта.)

  3. Метки: Сообщенная классификация объекта

function videoDetections = processDetections(visionData, videoDetections)
% The video sensor reports a classification value as an integer
% according to the following enumeration (starting from 0)
ClassificationValues = {'Unknown', 'Unknown Small', 'Unknown Big', ...
    'Pedestrian', 'Bike', 'Car', 'Truck', 'Barrier'};

% The total number of objects reported by the sensor in this frame
numVideoObjects = visionData.numObjects;

% The video objects are reported only 10 times per second, but the video
% has a frame rate of 20 frames per second. To prevent the annotations from
% flickering on and off, this function returns the values from the previous
% timestep if there are no video objects.
if numVideoObjects == 0
    if nargin == 1 % Returning a result even if there is no previous value
        videoDetections = struct('positions', {}, 'labels', {}, 'widths', {});
    end
    return;
else
    % Prepare a container for the relevant properties of video detections
    videoDetections = struct('positions', [], 'labels', [], 'widths', []);
    for i = 1:numVideoObjects
        videoDetections(i).widths = visionData.object(i).size(2);
        videoDetections(i).positions = visionData.object(i).position(1:2);
        videoDetections(i).labels = ClassificationValues{visionData.object(i).classification + 1};
    end
end
end

processLanes - считывает сообщенную информацию маршрута и преобразует ее в parabolicLaneBoundary объекты.

Контуры маршрута обновляются на основе laneReports из записей. Датчик сообщает полосы как параметры параболической модели:% y=ax2+bx+c

function laneBoundaries = processLanes(laneReports)
% Return processed lane boundaries

% Boundary type information
types = {'Unmarked', 'Solid', 'Dashed', 'Unmarked', 'BottsDots', ...
    'Unmarked', 'Unmarked', 'DoubleSolid'};

% Read the recorded lane reports for this frame
leftLane    = laneReports.left;
rightLane   = laneReports.right;

% Create parabolicLaneBoundary objects for left and right lane boundaries
leftParams = cast([leftLane.curvature, leftLane.headingAngle, leftLane.offset], 'double');
leftBoundaries = parabolicLaneBoundary(leftParams);
leftBoundaries.BoundaryType = types{leftLane.boundaryType};

rightParams  = cast([rightLane.curvature, rightLane.headingAngle, rightLane.offset], 'double');
rightBoundaries = parabolicLaneBoundary(rightParams);
rightBoundaries.BoundaryType = types{rightLane.boundaryType};

laneBoundaries = [leftBoundaries, rightBoundaries];
end

См. также

Приложения

Функции

Объекты

Похожие темы