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

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

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

function kalmanFilterForTracking

Введение

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

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

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

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

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

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

showDetections();

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

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

showTrajectory();

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

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

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

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

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

Используя видео, которое было просмотрено ранее, 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 hasFrame(utilities.videoReader)
    frame = readFrame(utilities.videoReader);

    % 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 showDetections()
  param = getDefaultParameters();
  utilities = createUtilities(param);
  trackedLocation = [];

  idx = 0;
  while hasFrame(utilities.videoReader)
    frame = readFrame(utilities.videoReader);
    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]), im2single(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(im2single(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]), im2single(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 = VideoReader('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