Отслеживание пешеходов от движущегося автомобиля

В этом примере показано, как отследить пешеходов, использующих камеру, смонтированную в движущемся автомобиле.

Обзор

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

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

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. Энная запись в pedScaleTable представляет предполагаемую высоту взрослого человека в пикселях. Индекс n ссылается на аппроксимированную Y-координату ног пешехода.

Чтобы получить такой вектор, набор учебных изображений был взят с той же точки зрения и в подобной сцене к тестовой среде. Обучение отображает содержавшие изображения пешеходов на различных расстояниях от камеры. Используя приложение Image Labeler, вручную аннотировались ограничительные рамки пешеходов в изображениях. Высота ограничительных рамок вместе с местоположением пешеходов в изображении использовалась, чтобы сгенерировать файл данных шкалы посредством регрессии. Вот функция помощника, чтобы показать алгоритмические шаги, чтобы подбирать модель линейной регрессии: helperTableOfScales.m

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

  • ROI : Необходимая область в форме [x, y, w, h]. Это ограничивает область обработки, чтобы основать местоположения.

  • scThresh : Порог допуска для оценки шкалы. Когда различие между обнаруженной шкалой и ожидаемой шкалой превышает допуск, обнаружение кандидата считается нереалистичным и удалено из выхода.

  • gatingThresh : Пропускание параметра для меры по расстоянию. Когда стоимость соответствия с обнаруженной ограничительной рамкой и предсказанной ограничительной рамкой превышает порог, система удаляет ассоциацию этих двух ограничительных рамок от отслеживания фактора.

  • gatingCost : Значение для матрицы стоимости присвоения, чтобы препятствовать возможному отслеживанию к присвоению обнаружения.

  • costOfNonAssignment : Значение для матрицы стоимости присвоения для того, чтобы не присваивать обнаружение или дорожку. Установка его слишком низко увеличивает вероятность создания нового трека и может привести к фрагментации дорожки. Установка его слишком высоко может привести к одноколейному пути, соответствующему серии отдельных движущихся объектов.

  • timeWindowSize : Количество кадров, требуемое оценить доверие дорожки.

  • confidenceThresh : Порог доверия, чтобы определить, является ли дорожка положительной истиной.

  • ageThresh : Минимальная длина дорожки, являющейся положительной истиной.

  • visThresh : Минимальный порог видимости, чтобы определить, является ли дорожка положительной истиной.

Создание системных объектов для инициализации системы слежения

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

pedScaleTable вектор, который хранится в файле данных шкалы, кодирует наши предварительные знания цели и сцены. Если вам обучили регрессор от ваших выборок, можно вычислить ожидаемую высоту в каждом возможном Y-position в изображении. Эти значения хранятся в векторе. Энная запись в 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 : Целочисленный ID дорожки.

  • color : Цвет дорожки для цели отображения.

  • bboxes : Матрица N-4, чтобы представлять ограничительные рамки объекта с текущим полем в последней строке. Каждая строка имеет форму [x, y, ширина, высота].

  • 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-2 с каждой строкой в форме [x, y].

  • bboxes : Матрица N-4 с каждой строкой в форме [x, y, ширина, высота].

  • 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 функционируйте использует версию Манкреса венгерского алгоритма, чтобы вычислить присвоение, которое минимизирует общую стоимость. Это возвращает 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