Детектор попадания мячика с использованием OpenCV



    Недавно мне довелось поучаствовать в одном интересном проекте. Моя сестра учится на дизайнера в БВШД, и им дали задание сделать проект на тему Street Interactive. Идея была выбрана довольно простая. На экране демонстрируется анимация движущегося медведя, всем желающим предлагается попасть в него из рогатки импровизированным снежком. Результат продемонстрирован на видео, кому интересна техническая реализация, добро пожаловать под кат.

    Описание

    Первоначальной идеей было использовать для трекинга кинект. Казалось, что для подобной задачи кинект подойдёт идеально — он обладает неплохой встроенной камерой, а так же позволяет ослеживать глубину (и определять с некоторой точностью положение тела в трёхмерном пространстве). Однако после непродолжительного тестирования от использования кинекта пришлось отказаться. Он не позволяет отслеживать объекты на дальнем расстоянии, а кроме того, яркий свет от проектора мешает его сенсорам.

    Затем у меня появилась идея использовать для трекинга обычную веб камеру. Расположить камеру рядом с проектором и направить на экран. С её помощью отслеживать положение мячика в плоскости экрана. Но оставалась ещё одна проблема — определить момент столкновения мячика со стенкой. В качестве варианта рассматривался Arduino с датчиком движения. Однако в итоге было решено использовать вторую камеру, расположенную вблизи экрана в качестве детектора движения. С помощью неё можно фиксировать момент, когда мячик подлетает к экрану, и брать координаты удара через несколько миллисекунд после этого момента.

    Программную реализацию было решено сделать на C++, используя библиотеку OpenCV. Она позволяет не изобретать велосипед, а пользоваться готовым функционалом для получения изображения с камеры, и последующей его обработке.

    Отслеживание мячика

    Для определения координат мячика, я воспользовался следующим алгоритмом.
    1) Перевёл изображение из RGB представления в HSV. Это облегчает определение похожих цветов, так как в отличие от RGB, HSV хранит в отдельном канале цветовой тон, насыщенность и яркость.
    2) Перевёл изображение в двоичное (bitmap). Те цвета, которые ближе всех к требуемуму цвету (цвету мячика) — преобразовались в белый. Остальные — в чёрный.
    3) Отфильтровал шумы медианным фильтром.
    4) Определил среднюю координату и количество белых пикселей. Если количество больше порогового значения — значит в кадре есть мячик.
    Получившийся код:

            clr=ballColor;
            frame=cvQueryFrame(capture); // Получаем изображение с камеры
            cvtColor(frame,frameHSV,CV_BGR2HSV); // Переводим в HSV
            inRange(frameHSV,Scalar(clr-ballThres,120,120),Scalar(clr+ballThres,255,255),frameBitmap);
                                                              // Переводим в bitmap
            medianBlur(frameBitmap,frameBitmap,5); // фильтруем шумы
            for(int i = screenLeft; i < screenRight; i++) {
                for(int j = screenUp; j < screenDown; j++) { 
                    cl=frameBitmap.at<char>(j,i);
                    if (cl!=0) { // Находим центр тяжести
                        x+=i;
                        y+=j;
                        n++;
                    }
                }
            }
            if (n>ballDifNum) { // Отсекаем некоторые случайные сробатывания
                x/=n;
                y/=n;
            }

    Определение удара

    Чтобы определить, что произошло столкновение я использовал вторую камеру в качестве детектора движения. Для этого я определял разницу между предыдущим кадром и текущим, и, если разница выше порогового значения — значит мячик влетел в кадр:

            frame=cvQueryFrame(captureHit);
            cvtColor(frame, frame, CV_RGB2GRAY );   // из цветного в серое
            GaussianBlur(frame,frameCurrent,cv::Size( 3, 3 ), -1);  // убираем шумы
            absdiff(frameCurrent,framePriv,mask);  // смотрим разницу между кадрами
            framePriv=frameCurrent.clone(); // сохраняем предыдущий кадр
            threshold(mask,mask,motionTreshold,255,cv::THRESH_BINARY);
            if (countNonZero(mask)>motionDifNum)
                  ttl3=detectDelay; // если мячик влетел в кадр, запускаем обратный отсчёт до столкновения


    Результат

    В итоге, имея координаты мячика и информацию о столкновении, программа передавала их во вторую программу, отвечающую за анимацию. Результат проекта представлен в первом видеоролике. А ниже вы можете посмотреть на тестирование технологии в домашних условиях.



    Ссылки

    balltracking.cpp — Полный листинг программы
    wikipedia.org — Об OpenCV на Википедии
    robocraft.ru — OpenCV шаг за шагом. Уроки OpenCV по-русски от Чеширского Кота.
    aishack.in — Tracking colored objects in OpenCV
    britishdesign.ru — Британская Высшая Школа Дизайна

    Комментарии 9

      0
      Что за музончик в видеоролике?
        +1
        Solar Sailer — Daft Punk?
          +1
          что-то меня этот трек задел, слушаю его уже день :-)
          +3
          Bondage Fairies — Levenus Supremus
          +5
          Как я мечтал о брате-программисте, когда делал свою интерактивную штуку для БВШД!
            +3
            Я до сих пор мечтаю о брате, пусть даже и не программисте.
            0
            Моя сестра учиться

            Она у Вас что делать?!
              0
              Имхо тут нужна была просто сетка из свето- и фотодиодов и плата ардуино
                +1
                Зато универсальность и переносимость. Нужны только камера, с которой в реальном времени снимается изображение, и компьютер.

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое