В этом примере показано, как отслеживать пешеходов с помощью камеры, установленной в движущемся автомобиле.
В этом примере показано, как выполнять автоматическое обнаружение и отслеживание людей на видео с движущейся камеры. Он демонстрирует гибкость системы слежения, адаптированной к движущейся камере, которая идеально подходит для приложений автомобильной безопасности. В отличие от стационарной камеры The Motion-Based Multiple Object Tracking, этот пример содержит несколько дополнительных алгоритмических шагов. Эти шаги включают обнаружение людей, настраиваемое подавление без максимума и эвристику для идентификации и устранения ложных дорожек тревоги. Дополнительные сведения см. в разделе Отслеживание нескольких объектов.
Этот пример - функция с основным корпусом в верхней части и вспомогательными подпрограммами в виде «Что такое вложенные функции?» ниже.
function PedestrianTrackingFromMovingCameraExample()
% Create system objects used for reading video, loading prerequisite data file, detecting pedestrians, and displaying the results. videoFile = 'vippedtracking.mp4'; scaleDataFile = 'pedScaleTable.mat'; % An auxiliary file that helps to determine the size of a pedestrian at different pixel locations. obj = setupSystemObjects(videoFile, scaleDataFile); detector = peopleDetectorACF('caltech'); % Create an empty array of tracks. tracks = initializeTracks(); % ID of the next track. nextId = 1; % Set the global parameters. option.ROI = [40 95 400 140]; % A rectangle [x, y, w, h] that limits the processing area to ground locations. option.scThresh = 0.3; % A threshold to control the tolerance of error in estimating the scale of a detected pedestrian. option.gatingThresh = 0.9; % A threshold to reject a candidate match between a detection and a track. option.gatingCost = 100; % A large value for the assignment cost matrix that enforces the rejection of a candidate match. option.costOfNonAssignment = 10; % A tuning parameter to control the likelihood of creation of a new track. option.timeWindowSize = 16; % A tuning parameter to specify the number of frames required to stabilize the confidence score of a track. option.confidenceThresh = 2; % A threshold to determine if a track is true positive or false alarm. option.ageThresh = 8; % A threshold to determine the minimum length required for a track being true positive. option.visThresh = 0.6; % A threshold to determine the minimum visibility value for a track being true positive. % Detect people and track them across video frames. stopFrame = 1629; % stop on an interesting frame with several pedestrians for fNum = 1:stopFrame frame = readFrame(obj.reader); [centroids, bboxes, scores] = detectPeople(); predictNewLocationsOfTracks(); [assignments, unassignedTracks, unassignedDetections] = ... detectionToTrackAssignment(); updateAssignedTracks(); updateUnassignedTracks(); deleteLostTracks(); createNewTracks(); displayTrackingResults(); % Exit the loop if the video player figure is closed. if ~isOpen(obj.videoPlayer) break; end end
![]()
Для этой системы отслеживания требуется файл данных, содержащий информацию, которая связывает местоположение пикселя на изображении с размером ограничивающей рамки, обозначающей местоположение пешехода. Эти предшествующие знания хранятся в векторе. pedScaleTable. n-я запись в pedScaleTable представляет оценочную высоту взрослого человека в пикселях. Индекс n ссылается на приблизительную координату Y ног пешехода.
Для получения такого вектора набор тренировочных изображений был взят с той же точки зрения и в той же сцене, что и среда тестирования. Обучающие изображения содержали изображения пешеходов на различных расстояниях от камеры. С помощью приложения Image Labeler ограничивающие рамки пешеходов на изображениях были аннотированы вручную. Высота ограничивающих рамок вместе с расположением пешеходов на изображении использовались для формирования файла данных масштаба посредством регрессии. Вот вспомогательная функция, показывающая алгоритмические шаги в соответствии с моделью линейной регрессии: helperTableOfScales.m
Существует также набор глобальных параметров, которые можно настроить для оптимизации производительности отслеживания. Приведенные ниже описания позволяют узнать, как эти параметры влияют на производительность отслеживания.
ROI Область интересов в форме [x, y, w, h]. Область обработки ограничивается местоположениями заземления.
scThresh Порог допуска для оценки шкалы. Когда разность между обнаруженной шкалой и ожидаемой шкалой превышает допуск, обнаружение кандидата считается нереалистичным и удаляется из выходных данных.
gatingThresh Параметр стробирования для измерения расстояния. Когда стоимость сопоставления обнаруженного ограничивающего прямоугольника и прогнозируемого ограничивающего прямоугольника превышает пороговое значение, система удаляет связь двух ограничивающих прямоугольников из учета отслеживания.
gatingCost Значение для матрицы затрат на назначение, чтобы предотвратить возможное отслеживание для определения назначения.
costOfNonAssignment Значение для матрицы затрат на назначение для отсутствия назначения обнаружения или дорожки. Установка слишком низкого значения увеличивает вероятность создания новой дорожки и может привести к фрагментации дорожки. Установка его слишком высоким может привести к одной дорожке, соответствующей ряду отдельных движущихся объектов.
timeWindowSize Количество кадров, необходимых для оценки достоверности дорожки.
confidenceThresh Порог достоверности для определения того, является ли трек истинно положительным.
ageThresh Минимальная длина трека является истинным позитивом.
visThresh Минимальный порог видимости для определения того, является ли дорожка истинно положительной.
setupSystemObjects создает системные объекты, используемые для чтения и отображения видеокадров, и загружает файл масштабных данных.
pedScaleTable вектор, который хранится в файле масштабных данных, кодирует наше предшествующее знание цели и сцены. После того, как регрессор обучен из ваших образцов, вы можете вычислить ожидаемую высоту в каждой возможной Y-позиции на изображении. Эти значения сохраняются в векторе. n-я запись в pedScaleTable представляет наш предполагаемый рост взрослого человека в пикселях. Индекс n ссылается на приблизительную координату Y ног пешехода.
function obj = setupSystemObjects(videoFile,scaleDataFile) % Initialize Video I/O % Create objects for reading a video from a file, drawing the % detected and tracked people in each frame, and playing the video. % Create a video file reader. obj.reader = VideoReader(videoFile); % Create a video player. obj.videoPlayer = vision.VideoPlayer('Position', [29, 597, 643, 386]); % Load the scale data file ld = load(scaleDataFile, 'pedScaleTable'); obj.pedScaleTable = ld.pedScaleTable; end
initializeTracks функция создает массив дорожек, где каждая дорожка представляет собой структуру, представляющую движущийся объект в видео. Целью структуры является поддержание состояния отслеживаемого объекта. Состояние состоит из информации, используемой для назначения обнаружения дорожке, завершения дорожки и отображения.
Структура содержит следующие поля:
id : Целочисленный идентификатор дорожки.
color : Цвет дорожки для целей отображения.
bboxes : Матрица N-by-4 для представления ограничивающих рамок объекта с текущим полем в последней строке. Каждая строка имеет вид [x, y, width, height].
scores Вектор N-by-1 для записи показателя классификации с детектора человека с текущим показателем обнаружения в последней строке.
kalmanFilter : Объект фильтра Калмана, используемый для отслеживания движения. Отслеживаем центральную точку объекта на изображении;
age : Количество кадров с момента инициализации дорожки.
totalVisibleCount Общее количество кадров, в которых обнаружен объект (видимый).
confidence : Пара из двух чисел, чтобы представить, насколько мы уверены, что доверяем треку. Он сохраняет максимальные и средние оценки обнаружения в прошлом в пределах предварительно определенного временного окна.
predPosition : Прогнозируемая ограничивающая рамка в следующем кадре.
function tracks = initializeTracks() % Create an empty array of tracks tracks = struct(... 'id', {}, ... 'color', {}, ... 'bboxes', {}, ... 'scores', {}, ... 'kalmanFilter', {}, ... 'age', {}, ... 'totalVisibleCount', {}, ... 'confidence', {}, ... 'predPosition', {}); end
detectPeople функция возвращает центроиды, ограничивающие рамки и оценки классификации обнаруженных людей. Выполняет фильтрацию и непревзойденное подавление на необработанном выходе детектора, возвращаемом peopleDetectorACF.
centroids : Матрица N-by-2 с каждой строкой в виде [x, y].
bboxes : Матрица N-by-4 с каждой строкой в виде [x, y, width, height].
scores Вектор N-by-1 с каждым элементом - это оценка классификации в соответствующем кадре.
function [centroids, bboxes, scores] = detectPeople() % Resize the image to increase the resolution of the pedestrian. % This helps detect people further away from the camera. resizeRatio = 1.5; frame = imresize(frame, resizeRatio, 'Antialiasing',false); % Run ACF people detector within a region of interest to produce % detection candidates. [bboxes, scores] = detect(detector, frame, option.ROI, ... 'WindowStride', 2,... 'NumScaleLevels', 4, ... 'SelectStrongest', false); % Look up the estimated height of a pedestrian based on location of their feet. height = bboxes(:, 4) / resizeRatio; y = (bboxes(:,2)-1) / resizeRatio + 1; yfoot = min(length(obj.pedScaleTable), round(y + height)); estHeight = obj.pedScaleTable(yfoot); % Remove detections whose size deviates from the expected size, % provided by the calibrated scale estimation. invalid = abs(estHeight-height)>estHeight*option.scThresh; bboxes(invalid, :) = []; scores(invalid, :) = []; % Apply non-maximum suppression to select the strongest bounding boxes. [bboxes, scores] = selectStrongestBbox(bboxes, scores, ... 'RatioType', 'Min', 'OverlapThreshold', 0.6); % Compute the centroids if isempty(bboxes) centroids = []; else centroids = [(bboxes(:, 1) + bboxes(:, 3) / 2), ... (bboxes(:, 2) + bboxes(:, 4) / 2)]; end end
Используйте фильтр Калмана для прогнозирования центроида каждой дорожки в текущем кадре и соответствующим образом обновите ее ограничивающую рамку. В качестве текущего прогноза размера берем ширину и высоту ограничивающей рамки в предыдущем кадре.
function predictNewLocationsOfTracks() for i = 1:length(tracks) % Get the last bounding box on this track. bbox = tracks(i).bboxes(end, :); % Predict the current location of the track. predictedCentroid = predict(tracks(i).kalmanFilter); % Shift the bounding box so that its center is at the predicted location. tracks(i).predPosition = [predictedCentroid - bbox(3:4)/2, bbox(3:4)]; end end
Назначение обнаружений объектов в текущем кадре существующим дорожкам выполняется путем минимизации затрат. Стоимость вычисляется с помощью bboxOverlapRatio функция и является коэффициентом перекрытия между прогнозируемой ограничивающей рамкой и обнаруженной ограничивающей рамкой. В этом примере предполагается, что человек будет двигаться постепенно в последовательных кадрах из-за высокой частоты кадров видео и низкой скорости движения человека.
Алгоритм состоит из двух этапов:
Шаг 1: Расчет стоимости назначения каждого обнаружения каждой дорожке с помощью bboxOverlapRatio мера. Когда люди движутся к камере или от нее, их движение не будет точно описано только точкой центроида. Стоимость учитывает расстояние на плоскости изображения, а также масштаб ограничивающих рамок. Это предотвращает назначение обнаружений далеко от камеры для дорожек ближе к камере, даже если их центроиды совпадают. Выбор этой функции затрат облегчит вычисление, не прибегая к более сложной динамической модели. Результаты сохраняются в матрице MxN, где M - количество дорожек, а N - количество обнаружений.
Шаг 2: Решение проблемы назначения, представленной матрицей затрат, с помощью assignDetectionsToTracks функция. Функция принимает матрицу затрат и затраты на то, чтобы не назначать никаких обнаружений дорожке.
Значение стоимости отсутствия назначения обнаружения дорожке зависит от диапазона значений, возвращаемых функцией затрат. Это значение должно быть настроено экспериментально. Установка слишком низкого значения увеличивает вероятность создания новой дорожки и может привести к фрагментации дорожки. Установка его слишком высоким может привести к одной дорожке, соответствующей ряду отдельных движущихся объектов.
assignDetectionsToTracks функция использует версию Munkres венгерского алгоритма для вычисления назначения, которое минимизирует общую стоимость. Он возвращает матрицу M x 2, содержащую соответствующие индексы назначенных дорожек и обнаружений в двух его столбцах. Он также возвращает индексы дорожек и обнаружений, которые остались неназначенными.
function [assignments, unassignedTracks, unassignedDetections] = ... detectionToTrackAssignment() % Compute the overlap ratio between the predicted boxes and the % detected boxes, and compute the cost of assigning each detection % to each track. The cost is minimum when the predicted bbox is % perfectly aligned with the detected bbox (overlap ratio is one) predBboxes = reshape([tracks(:).predPosition], 4, [])'; cost = 1 - bboxOverlapRatio(predBboxes, bboxes); % Force the optimization step to ignore some matches by % setting the associated cost to be a large number. Note that this % number is different from the 'costOfNonAssignment' below. % This is useful when gating (removing unrealistic matches) % technique is applied. cost(cost > option.gatingThresh) = 1 + option.gatingCost; % Solve the assignment problem. [assignments, unassignedTracks, unassignedDetections] = ... assignDetectionsToTracks(cost, option.costOfNonAssignment); end
updateAssignedTracks функция обновляет каждую назначенную дорожку с соответствующим обнаружением. Он вызывает correct способ vision.KalmanFilter для корректировки оценки местоположения. Далее он сохраняет новую ограничивающую рамку, беря среднее значение размера последних (до) 4 коробок, и увеличивает возраст дорожки и общее количество видимых элементов на 1. Наконец, функция корректирует нашу оценку достоверности для дорожки на основе предыдущих показателей обнаружения.
function updateAssignedTracks() numAssignedTracks = size(assignments, 1); for i = 1:numAssignedTracks trackIdx = assignments(i, 1); detectionIdx = assignments(i, 2); centroid = centroids(detectionIdx, :); bbox = bboxes(detectionIdx, :); % Correct the estimate of the object's location % using the new detection. correct(tracks(trackIdx).kalmanFilter, centroid); % Stabilize the bounding box by taking the average of the size % of recent (up to) 4 boxes on the track. T = min(size(tracks(trackIdx).bboxes,1), 4); w = mean([tracks(trackIdx).bboxes(end-T+1:end, 3); bbox(3)]); h = mean([tracks(trackIdx).bboxes(end-T+1:end, 4); bbox(4)]); tracks(trackIdx).bboxes(end+1, :) = [centroid - [w, h]/2, w, h]; % Update track's age. tracks(trackIdx).age = tracks(trackIdx).age + 1; % Update track's score history tracks(trackIdx).scores = [tracks(trackIdx).scores; scores(detectionIdx)]; % Update visibility. tracks(trackIdx).totalVisibleCount = ... tracks(trackIdx).totalVisibleCount + 1; % Adjust track confidence score based on the maximum detection % score in the past 'timeWindowSize' frames. T = min(option.timeWindowSize, length(tracks(trackIdx).scores)); score = tracks(trackIdx).scores(end-T+1:end); tracks(trackIdx).confidence = [max(score), mean(score)]; end end
updateUnassignedTracks функция помечает каждую неназначенную дорожку как невидимую, увеличивает ее возраст на 1 и добавляет к дорожке предсказанную ограничительную рамку. Уверенность установлена на ноль, так как мы не уверены, почему она не была назначена треку.
function updateUnassignedTracks() for i = 1:length(unassignedTracks) idx = unassignedTracks(i); tracks(idx).age = tracks(idx).age + 1; tracks(idx).bboxes = [tracks(idx).bboxes; tracks(idx).predPosition]; tracks(idx).scores = [tracks(idx).scores; 0]; % Adjust track confidence score based on the maximum detection % score in the past 'timeWindowSize' frames T = min(option.timeWindowSize, length(tracks(idx).scores)); score = tracks(idx).scores(end-T+1:end); tracks(idx).confidence = [max(score), mean(score)]; end end
deleteLostTracks удаляет дорожки, которые были невидимы слишком много последовательных кадров. Он также удаляет недавно созданные дорожки, которые были невидимы для многих кадров в целом.
Шумные обнаружения, как правило, приводят к созданию ложных дорожек. В этом примере мы удаляем дорожку при следующих условиях:
Объект отслеживался в течение короткого времени. Обычно это происходит, когда появляется ложное обнаружение для нескольких кадров и для него инициируется дорожка.
Трасса была отмечена невидимой для большинства кадров.
Он не смог получить сильное обнаружение в течение последних нескольких кадров, которое выражается как максимальный показатель достоверности обнаружения.
function deleteLostTracks() if isempty(tracks) return; end % Compute the fraction of the track's age for which it was visible. ages = [tracks(:).age]'; totalVisibleCounts = [tracks(:).totalVisibleCount]'; visibility = totalVisibleCounts ./ ages; % Check the maximum detection confidence score. confidence = reshape([tracks(:).confidence], 2, [])'; maxConfidence = confidence(:, 1); % Find the indices of 'lost' tracks. lostInds = (ages <= option.ageThresh & visibility <= option.visThresh) | ... (maxConfidence <= option.confidenceThresh); % Delete lost tracks. tracks = tracks(~lostInds); end
Создание новых дорожек из неназначенных обнаружений. Предположим, что любое неназначенное обнаружение является началом новой дорожки. На практике можно использовать другие сигналы для устранения шумных обнаружений, таких как размер, местоположение или внешний вид.
function createNewTracks() unassignedCentroids = centroids(unassignedDetections, :); unassignedBboxes = bboxes(unassignedDetections, :); unassignedScores = scores(unassignedDetections); for i = 1:size(unassignedBboxes, 1) centroid = unassignedCentroids(i,:); bbox = unassignedBboxes(i, :); score = unassignedScores(i); % Create a Kalman filter object. kalmanFilter = configureKalmanFilter('ConstantVelocity', ... centroid, [2, 1], [5, 5], 100); % Create a new track. newTrack = struct(... 'id', nextId, ... 'color', 255*rand(1,3), ... 'bboxes', bbox, ... 'scores', score, ... 'kalmanFilter', kalmanFilter, ... 'age', 1, ... 'totalVisibleCount', 1, ... 'confidence', [score, score], ... 'predPosition', bbox); % Add it to the array of tracks. tracks(end + 1) = newTrack; %#ok<AGROW> % Increment the next id. nextId = nextId + 1; end end
displayTrackingResults функция рисует цветную ограничительную рамку для каждой дорожки на видеокадре. Уровень прозрачности коробки вместе с отображаемой оценкой указывают на достоверность обнаружений и дорожек.
function displayTrackingResults() displayRatio = 4/3; frame = imresize(frame, displayRatio); if ~isempty(tracks) ages = [tracks(:).age]'; confidence = reshape([tracks(:).confidence], 2, [])'; maxConfidence = confidence(:, 1); avgConfidence = confidence(:, 2); opacity = min(0.5,max(0.1,avgConfidence/3)); noDispInds = (ages < option.ageThresh & maxConfidence < option.confidenceThresh) | ... (ages < option.ageThresh / 2); for i = 1:length(tracks) if ~noDispInds(i) % scale bounding boxes for display bb = tracks(i).bboxes(end, :); bb(:,1:2) = (bb(:,1:2)-1)*displayRatio + 1; bb(:,3:4) = bb(:,3:4) * displayRatio; frame = insertShape(frame, ... 'FilledRectangle', bb, ... 'Color', tracks(i).color, ... 'Opacity', opacity(i)); frame = insertObjectAnnotation(frame, ... 'rectangle', bb, ... num2str(avgConfidence(i)), ... 'Color', tracks(i).color); end end end frame = insertShape(frame, 'Rectangle', option.ROI * displayRatio, ... 'Color', [255, 0, 0], 'LineWidth', 3); step(obj.videoPlayer, frame); end
end