Распознавание движения на OpenCV

В предыдущей статье я рассмотрел математическую модель камеры, необходимую для проведения измерений. Теперь давайте попробуем камеру заставить распознавать движение. Это может понадобиться при разработке робота, реагирующего на движение внешних объектов. Детектор движения реализуем на C++ при помощи библиотеки OpenCV 2.3.

 
 
 
 
 
 

Сама идея алгоритма будет достаточно проста.
1. Сглаживаем текущий кадр (currentFrame) фильтром Гаусса, чтобы избавиться от шумов.

cv::GaussianBlur( currentFrame, m_bluredImage, cv::Size( 3, 3 ), -1 );

2. Из сглаженного текущего кадра (m_bluredImage ) вычитаем предыдущий (m_previousImage). Если искомые изображения были в градациях серого, то и значения пикселей в разности (mask) будут изменяться от нуля до 255(при восьми битной глубине цвета).

cv::absdiff( m_bluredImage, m_previousImage, mask );

3. Сравниваем значения полученной разности с некоторым пороговым значением (MOTION_THRESHOLD). Если значение пикселя больше порогового, то этот пиксель принадлежит движущемуся объекту, иначе отбрасываем его. Теперь мы получили бинарное изображение (mask), где ноль означает, что пиксель не движется, отличное от нуля значение – пиксель движется.

cv::threshold( mask, mask,  MOTION_THRESHOLD, 255, cv::THRESH_BINARY );

4. Применяем морфологические операции закрытия и открытия, чтобы избавиться от движущихся регионов малого размера (шумы камеры). Полученное изображение (mask) и есть движущийся контур.

cv::morphologyEx( mask, mask, cv::MORPH_CLOSE, cv::Mat() );
cv::morphologyEx( mask, mask, cv::MORPH_OPEN, m_openingKernel, cv::Point( -1, -1 ), 1, cv::BORDER_CONSTANT, cv::Scalar( 0 ) );

5. Наносим бинарное изображение на так называемое изображение истории движения (motionHistoryImage). На нём нарисованы движущиеся контуры за последние, например, 200 мс (m_motionHistoryDuration). Контуры были получены через постоянные промежутки времени. Интенсивность пикселей контура обратно пропорциональна времени, которое прошло от измерения контура до данного момента. Т.е. чем раньше был получен движущийся контур, тем он бледнее изображён на изображение истории движения.

cv::updateMotionHistory( mask, m_motionHistoryImage, timestamp, m_motionHistoryDuration );

6. Выделяем регионы (targets) с различными движениями на изображение истории движения.

cv::segmentMotion( m_motionHistoryImage, m_segmask, targets, timestamp, m_maxMotionGradient );

7. Отбрасываем все регионы, площадь которых меньше некоторого значения (MIN_CONTOUR_AREA).

std::vector<cv::Rect>::iterator endIt = targets.end( );
for( std::vector<cv::Rect>::iterator it = targets.begin( ); it != endIt; ++it ) {
    if( cv::countNonZero( mask( * it ) ) < MIN_CONTOUR_AREA )
        targets.erase( it );
}

9. Сохраняем сглаженный текущий кадр как предыдущий кадр.

m_bluredImage.copyTo( m_previousImage );

Приведу результаты работы алгоритма.
Кадр из видео потока с камеры (рука движется параллельно камере):

Результаты детектора движения (прямоугольниками обведены отдельные движущиеся зоны ):

Скачать детектор движения.

2 Comments

  1. botru:

    Здравствуйте!
    Пытаюсь запустить в VisualStudio ваш проект, выходит ошибка

    include\vector Line: 116
    Expression: ("this->_Has_container()",0)

    что-то не нравится в блоке:

    std::vector::iterator endIt = targets.end( );
    for( std::vector::iterator it = targets.begin( ); it != endIt; ++it ) {
    if( cv::countNonZero( mask( * it ) ) < MIN_CONTOUR_AREA )
    targets.erase( it );
    }

    Не подскажите, что у меня не так? Почему не работает ?
    Картинка появляется, но выходит эта ошибка и дальше ничего нельзя сделать.

    Спасибо.

  2. Попробуйте написать:
    ...
    auto endIt = targets.end( );
    for( auto it = targets.begin( ); it != endIt; ++it ) {
    ...
    Но нужна новая Visual Studio с поддержкой C++11.

Leave a Reply

You must be logged in to post a comment.