Используя фильтр Калмана для объектного отслеживания

Этот пример показывает, как использовать объект vision.KalmanFilter и функцию configureKalmanFilter к отслеживаемым объектам.

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

function kalmanFilterForTracking

Введение

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

  • Прогноз будущего местоположения объекта

  • Сокращение шума введено неточными обнаружениями

  • Упрощение процесса ассоциации нескольких объектов к их дорожкам

Проблемы объектного отслеживания

Прежде, чем показать использование Фильтра Калмана, давайте сначала исследуем проблемы отслеживания объекта в видео. Следующее видео показывает зеленый шар, перемещающийся слева направо в пол.

showDetections();

Белая область по шару подсвечивает пиксели, обнаруженные с помощью vision.ForegroundDetector, который разделяет движущиеся объекты от фона. Фоновое вычитание только находит фрагмент шара из-за низкого контраста между шаром и полом. Другими словами, процесс обнаружения не идеален и вводит шум.

Чтобы легко визуализировать целую объектную траекторию, мы накладываем все кадры видео на одно изображение. "+" метки указывают на центроиды, вычисленные с помощью анализа блоба.

showTrajectory();

Могут наблюдаться две проблемы:

  1. Центр области обычно отличается от центра шара. Другими словами, существует ошибка в измерении местоположения шара.

  2. Местоположение шара не доступно, когда это закрывается полем, т.е. измерение отсутствует.

Обе из этих проблем могут быть обращены при помощи Фильтра Калмана.

Отследите отдельный объект Используя фильтр Калмана

Используя видео, которое было просмотрено ранее, функция trackSingleObject показывает вам как:

  • Создайте vision.KalmanFilter при помощи configureKalmanFilter

  • Используйте predict и методы correct в последовательности, чтобы устранить шум, существующий в системе слежения

  • Используйте метод predict отдельно, чтобы оценить местоположение шара, когда это будет закрыто полем

Выбор параметров Фильтра Калмана может быть сложным. Функция configureKalmanFilter помогает упростить эту проблему. Больше деталей об этом может быть найдено далее в примере.

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

frame            = [];  % A video frame
detectedLocation = [];  % The detected location
trackedLocation  = [];  % The tracked location
label            = '';  % Label for the ball
utilities        = [];  % Utilities used to process the video

Процедуру для отслеживания отдельного объекта показывают ниже.

function trackSingleObject(param)
  % Create utilities used for reading video, detecting moving objects,
  % and displaying the results.
  utilities = createUtilities(param);

  isTrackInitialized = false;
  while ~isDone(utilities.videoReader)
    frame = readFrame();

    % Detect the ball.
    [detectedLocation, isObjectDetected] = detectObject(frame);

    if ~isTrackInitialized
      if isObjectDetected
        % Initialize a track by creating a Kalman filter when the ball is
        % detected for the first time.
        initialLocation = computeInitialLocation(param, detectedLocation);
        kalmanFilter = configureKalmanFilter(param.motionModel, ...
          initialLocation, param.initialEstimateError, ...
          param.motionNoise, param.measurementNoise);

        isTrackInitialized = true;
        trackedLocation = correct(kalmanFilter, detectedLocation);
        label = 'Initial';
      else
        trackedLocation = [];
        label = '';
      end

    else
      % Use the Kalman filter to track the ball.
      if isObjectDetected % The ball was detected.
        % Reduce the measurement noise by calling predict followed by
        % correct.
        predict(kalmanFilter);
        trackedLocation = correct(kalmanFilter, detectedLocation);
        label = 'Corrected';
      else % The ball was missing.
        % Predict the ball's location.
        trackedLocation = predict(kalmanFilter);
        label = 'Predicted';
      end
    end

    annotateTrackedObject();
  end % while

  showTrajectory();
end

Существует два отличных сценария, к которым обращается Фильтр Калмана:

  • Когда шар обнаруживается, Фильтр Калмана сначала предсказывает свое состояние в текущем кадре видео, и затем использует недавно обнаруженное объектное местоположение, чтобы исправить его состояние. Это производит отфильтрованное местоположение.

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

Вы видите траекторию шара путем накладывания всех кадров видео.

param = getDefaultParameters();  % get Kalman configuration that works well
                                 % for this example

trackSingleObject(param);  % visualize the results

Исследуйте параметры конфигурации фильтра Калмана

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

Функция configureKalmanFilter возвращает объект Фильтра Калмана. Необходимо обеспечить пять входных параметров.

kalmanFilter = configureKalmanFilter(MotionModel, InitialLocation,
         InitialEstimateError, MotionNoise, MeasurementNoise)

Установка MotionModel должна соответствовать физическим характеристикам движения объекта. Можно установить его или на постоянную скорость или на постоянную ускоряющую модель. Следующий пример иллюстрирует последствия совершения субоптимального выбора.

param = getDefaultParameters();         % get parameters that work well
param.motionModel = 'ConstantVelocity'; % switch from ConstantAcceleration
                                        % to ConstantVelocity
% After switching motion models, drop noise specification entries
% corresponding to acceleration.
param.initialEstimateError = param.initialEstimateError(1:2);
param.motionNoise          = param.motionNoise(1:2);

trackSingleObject(param); % visualize the results

Заметьте, что шар появился в месте, которое очень отличается от предсказанного местоположения. Со времени, когда шар был выпущен, он подвергся постоянному замедлению из-за сопротивления от ковра. Поэтому постоянная ускоряющая модель была лучшим выбором. Если бы вы сохранили постоянную скоростную модель, результаты отслеживания были бы субоптимальными, неважно, что вы выбрали для других значений.

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

param = getDefaultParameters();  % get parameters that work well
param.initialLocation = [0, 0];  % location that's not based on an actual detection
param.initialEstimateError = 100*ones(1,3); % use relatively small values

trackSingleObject(param); % visualize the results

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

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

param = getDefaultParameters();
param.segmentationThreshold = 0.0005; % smaller value resulting in noisy detections
param.measurementNoise      = 12500;  % increase the value to compensate
                                      % for the increase in measurement noise

trackSingleObject(param); % visualize the results

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

Теперь, когда вы знакомы с тем, как использовать Фильтр Калмана и как сконфигурировать его, следующий раздел поможет вам изучить, как он может использоваться для нескольких, возражают отслеживанию.

Примечание: В порядке упростить процесс настройки в вышеупомянутых примерах, мы использовали функцию configureKalmanFilter. Эта функция делает несколько предположений. См. документацию функции для деталей. Если вы требуете большего уровня управления процессом настройки, можно использовать объект vision.KalmanFilter непосредственно.

Отследите несколько объектов Используя фильтр Калмана

Отслеживание нескольких объектов ставит несколько дополнительных проблем:

  • Несколько обнаружений должны быть сопоставлены с правильными дорожками

  • Необходимо обработать новые объекты, появляющиеся в сцене

  • Объектная идентичность должна сохраняться, когда несколько объектов объединяют в одно обнаружение

Объект vision.KalmanFilter вместе с функцией assignDetectionsToTracks может помочь решить проблемы

  • Присвоение обнаружений к дорожкам

  • Определение, соответствует ли обнаружение новому объекту, другими словами, созданию дорожки

  • Так же, как в случае закрытого отдельного объекта, прогноз может использоваться, чтобы помочь отдельным объектам, которые являются друг близко к другу

Чтобы узнать больше об использовании Фильтра Калмана, чтобы отследить несколько объектов, смотрите пример, названный Основанный на движении Несколько Объектное Отслеживание.

Служебные функции, используемые в примере

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

Получите параметры по умолчанию для создания Фильтра Калмана и для сегментации шара.

function param = getDefaultParameters
  param.motionModel           = 'ConstantAcceleration';
  param.initialLocation       = 'Same as first detection';
  param.initialEstimateError  = 1E5 * ones(1, 3);
  param.motionNoise           = [25, 10, 1];
  param.measurementNoise      = 25;
  param.segmentationThreshold = 0.05;
end

Считайте следующий кадр видео из видеофайла.

function frame = readFrame()
  frame = step(utilities.videoReader);
end

Обнаружьте и аннотируйте шар в видео.

function showDetections()
  param = getDefaultParameters();
  utilities = createUtilities(param);
  trackedLocation = [];

  idx = 0;
  while ~isDone(utilities.videoReader)
    frame = readFrame();
    detectedLocation = detectObject(frame);
    % Show the detection result for the current video frame.
    annotateTrackedObject();

    % To highlight the effects of the measurement noise, show the detection
    % results for the 40th frame in a separate figure.
    idx = idx + 1;
    if idx == 40
      combinedImage = max(repmat(utilities.foregroundMask, [1,1,3]), frame);
      figure, imshow(combinedImage);
    end
  end % while

  % Close the window which was used to show individual video frame.
  uiscopes.close('All');
end

Обнаружьте шар в текущем кадре видео.

function [detection, isObjectDetected] = detectObject(frame)
  grayImage = rgb2gray(frame);
  utilities.foregroundMask = step(utilities.foregroundDetector, grayImage);
  detection = step(utilities.blobAnalyzer, utilities.foregroundMask);
  if isempty(detection)
    isObjectDetected = false;
  else
    % To simplify the tracking process, only use the first detected object.
    detection = detection(1, :);
    isObjectDetected = true;
  end
end

Покажите текущее обнаружение и отслеживающие результаты.

function annotateTrackedObject()
  accumulateResults();
  % Combine the foreground mask with the current video frame in order to
  % show the detection result.
  combinedImage = max(repmat(utilities.foregroundMask, [1,1,3]), frame);

  if ~isempty(trackedLocation)
    shape = 'circle';
    region = trackedLocation;
    region(:, 3) = 5;
    combinedImage = insertObjectAnnotation(combinedImage, shape, ...
      region, {label}, 'Color', 'red');
  end
  step(utilities.videoPlayer, combinedImage);
end

Покажите траекторию шара путем накладывания всех кадров видео друг на друге.

function showTrajectory
  % Close the window which was used to show individual video frame.
  uiscopes.close('All');

  % Create a figure to show the processing results for all video frames.
  figure; imshow(utilities.accumulatedImage/2+0.5); hold on;
  plot(utilities.accumulatedDetections(:,1), ...
    utilities.accumulatedDetections(:,2), 'k+');

  if ~isempty(utilities.accumulatedTrackings)
    plot(utilities.accumulatedTrackings(:,1), ...
      utilities.accumulatedTrackings(:,2), 'r-o');
    legend('Detection', 'Tracking');
  end
end

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

function accumulateResults()
  utilities.accumulatedImage      = max(utilities.accumulatedImage, frame);
  utilities.accumulatedDetections ...
    = [utilities.accumulatedDetections; detectedLocation];
  utilities.accumulatedTrackings  ...
    = [utilities.accumulatedTrackings; trackedLocation];
end

В целях рисунка выберите начальное местоположение, используемое Фильтром Калмана.

function loc = computeInitialLocation(param, detectedLocation)
  if strcmp(param.initialLocation, 'Same as first detection')
    loc = detectedLocation;
  else
    loc = param.initialLocation;
  end
end

Создайте утилиты для чтения видео, обнаружения движущихся объектов и отображения результатов.

function utilities = createUtilities(param)
  % Create System objects for reading video, displaying video, extracting
  % foreground, and analyzing connected components.
  utilities.videoReader = vision.VideoFileReader('singleball.mp4');
  utilities.videoPlayer = vision.VideoPlayer('Position', [100,100,500,400]);
  utilities.foregroundDetector = vision.ForegroundDetector(...
    'NumTrainingFrames', 10, 'InitialVariance', param.segmentationThreshold);
  utilities.blobAnalyzer = vision.BlobAnalysis('AreaOutputPort', false, ...
    'MinimumBlobArea', 70, 'CentroidOutputPort', true);

  utilities.accumulatedImage      = 0;
  utilities.accumulatedDetections = zeros(0, 2);
  utilities.accumulatedTrackings  = zeros(0, 2);
end
end