В этом примере показано, как использовать vision.KalmanFilter
объект и configureKalmanFilter
функционируйте к отслеживаемым объектам.
Этим примером является функция со своей основной частью наверху и стандартными программами помощника в форме вложенных функций.
function kalmanFilterForTracking
Фильтр Калмана имеет много использования, включая приложения в управлении, навигации, компьютерном зрении и эконометрике временных рядов. Этот пример иллюстрирует, как использовать Фильтр Калмана для отслеживания объектов и особого внимания на трех важных функциях:
Предсказание будущего местоположения объекта
Сокращение шума введено неточными обнаружениями
Упрощение процесса ассоциации нескольких объектов к их дорожкам
Прежде, чем показать использование Фильтра Калмана, давайте сначала исследуем проблемы отслеживания объекта в видео. Следующее видео показывает зеленый мяч, перемещающийся слева направо в пол.
showDetections();
Белая область по мячу подсвечивает пиксели, обнаруженные с помощью vision.ForegroundDetector
, который разделяет движущиеся объекты от фона. Фоновое вычитание только находит фрагмент мяча из-за низкого контраста между мячом и полом. Другими словами, процесс обнаружения не идеален и вводит шум.
Чтобы легко визуализировать целую объектную траекторию, мы накладываем все видеокадры на одно изображение. "+" метки указывают на центроиды, вычисленные с помощью анализа блоба.
showTrajectory();
Могут наблюдаться две проблемы:
Центр области обычно отличается от центра мяча. Другими словами, существует ошибка в измерении местоположения мяча.
Местоположение мяча не доступно, когда это закрывается полем, 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