Настройте и используйте 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')
Получите соответствующие записанные данные.
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')
Чтобы отобразить видеоклип с аннотациями, просто повторите кадр за кадром аннотации. На видео видно, что машина немного шагает вверх и вниз, что изменяет угол тангажа. Не было сделано никаких попыток компенсировать это движение тангажа. В результате преобразование координат транспортного средства в координаты изображения немного неточно на некоторых системах координат.
% 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
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
The updateDisplay
функция отображает все аннотации объектов в верхней части видеокадра.
Обновление отображения включает следующие шаги:
Использование monoCamera
датчик для преобразования сообщенных обнаружений в ограничительные рамки и аннотации системы координат.
Использование 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
массивы. Этот пример использует только следующие два массива:
laneReports
, а struct
массив, который сообщает о контурах маршрута. В нем есть следующие поля: left
и right
. Каждый элемент массива соответствует другому временному шагу. Оба left
и right
являются структурами с этими полями: isValid
, confidence
, boundaryType
, offset
, headingAngle
, и curvature
.
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 - считывает зарегистрированные обнаружения зрения. В этом примере извлекаются только следующие свойства:
Положение: Двумерная [x, y]
массив в координатах транспортного средства
Ширина: Ширина объекта, как сообщает видеодатчик (Примечание: Датчик не сообщает никаких других размерностей размера объекта.)
Метки: Сообщенная классификация объекта
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
из записей. Датчик сообщает полосы как параметры параболической модели:%
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