Пользователь
18,5
рейтинг
31 июля 2012 в 10:26

Разработка → Программный захват с вебкамеры

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


Итак, есть у нас вебкамера и Питон. Прежде всего, нельзя не похвалить камеру, которую я приобрел в dealextreme: малютка размером с половину спичечного коробка, работает в Винде и Линуксе без установки стороннего ПО (проверял в Вин 7 и Минт 13, включил — и поехали), приемлемое качество и цена. При работе камера не выдает себя горящими индикаторами или другими эффектами. Малые размеры способствуют незаметной ее установке.


Поскольку я работаю как в Винде, так и в Линуксе, решение должно удовлетворять обоим ОС. Поможет библиотека компьютерного зрения OpenCV и ее биндинг для Питона. Альтернативным решением может быть video4linux. Но, во-первых, мне было интересно освоить OpenCV, а во-вторых, про использование v4l уже была отличная хабрастатья.

Установим библиотеки numpy и opencv. Пользователи Линукса открывают терминал и пишут:

sudo apt-get install python-numpy
sudo apt-get install python-opencv


Те, кто под Виндой, идут сюда и выкачивают дистрибутивы numpy и opencv под нужную версию Питона.

Для Винды есть альтернативный способ установки. Достаточно скачать официальный дистрибутив библиотеки. В архиве обнаруживаем /build/python/2.x/cv2.pyd. Кидаем его в site-packages. Там же создаем файл cv.py с содержимым:

from cv2.cv import *


Все готово. Тестовый импорт:
import cv


Мы будем использовать часть библиотеки под названием higui. Начинаем эксперименты. Пробный шар — получение кадра и запись его в файл:

import cv

capture = cv.CaptureFromCAM(0)
frame = cv.QueryFrame(capture)
cv.SaveImage("capture.jpg", frame)


Функция CaptureFromCAM создает объект, с которого будет происходить захват. 0 — это индекс устройства, он может быть больше нуля, если камер несколько. Значение -1 несет смысл «любая доступная камера».

Теперь можно запустить сбор кадров в цикле. Будет замечательно, если кадры будут попутно отображаться в отдельном окне:

import cv

capture = cv.CaptureFromCAM(-1)
cv.NamedWindow("capture", cv.CV_WINDOW_AUTOSIZE)

i = 0
while True:
    frame = cv.QueryFrame(capture)
    cv.ShowImage("capture", frame)
    cv.WaitKey(10)
    path = "capture%.4d.jpg" % i # Уникальное имя для каждого кадра
    cv.SaveImage(path, frame)
    i += 1


Функция NamedWindow создает и отображает окно, в которые выводится в цикле каждый кадр. Обработчик WaitKey задает задержку в миллисекундах для обработки событий окна, например, нажатия клавиши или вывода изображения. Если его опустить, окно не будет отображать кадры (и, возможно, вообще не появится).


Захват кадров в действии. Камера смотрит в монитор, поэтому возникает рекурсия — декстоп, внутри которого десктоп, внутри которого...

Сбор множества кадров полезен, когда требуется вставлять между снимками временную паузу, например, 1 кадр каждую минуту. Достаточно подставить в цикл time.sleep(60).

Если же требуется непрерывная съемка, нужно записывать кадры в видеопоток.

import cv

capture = cv.CaptureFromCAM(-1)
fourcc = cv.CV_FOURCC('M','J','P','G')
fps = 16
w, h = 640, 480
stream = cv.CreateVideoWriter("test.avi", fourcc, fps, (w, h))
while True:
    frame = cv.QueryFrame(capture)
    cv.WriteFrame(stream, frame)


На первый взгляд, здесь тоже все очевидно: функция CreateVideoWriter создает поток, в который пишутся кадры. Достаточно прервать цикл, чтобы получить готовый видеофайл. Однако, следует разобраться с входными параметрами.

fourcc — это кодек, целое число, результат отображения четырехсимвольного имени кодека в числовой индекс. Например, CV_FOURCC('P','I','M,'1') — это сжатие MPEG-1. В Винде можно передать -1 для выбора кодека интерактивно в диалоговом окне или 0 для записи без сжатия (размер файла получится ого-го!). Хабраюзер Elsedar подсказывает, где можно посмотреть полный список кодеков: www.fourcc.org/codecs.php

fps — частота кадров в секунду. Параметр напрямую зависит от модели вашей камеры. На это месте реальное положение дел пошло вразрез с документацией. Полагается, что параметры записи должны устанавливаться не на глазок, а точным образом, для чего служит функция GetCaptureProperty. Она может получить параметры девайса для корректной инициализации захвата. Но если высота и ширина кадра извлекаются без проблем, то получение fps:

fps = cv.GetCaptureProperty(capture, cv.CV_CAP_PROP_FPS)


возвращает -1, что не удовлетворит функцию CreateVideoWriter, поэтому частота подбирается эмпирически. Как правило, у большинства камер она колеблется от 14 до 16 кадров в секунду. Если выставить 25, то полученный файл будет напоминать немое кино 20-х годов.

Последний параметр frame_size — пара целых чисел высоты и ширины кадра. Чтобы использовать параметры камеры, инициализируйте их следующим образом:

w = cv.GetCaptureProperty(capture, cv.CV_CAP_PROP_FRAME_WIDTH)
h = cv.GetCaptureProperty(capture, cv.CV_CAP_PROP_FRAME_HEIGHT)
w, h = int(w), int(h) # По умолчанию возвращаются как float


Теперь можно затронуть тему цветокоррекции. Если выходные кадры вас не устраивают, вполне разумно подкорректировать параметры захвата, чем пакетно перегонять горы джипегов или часы видео. Для установки параметров есть функция SetCaptureProperty. Заполнять несколько свойств удобно перебором словаря:

config = {
	cv.CV_CAP_PROP_BRIGHTNESS: 50,
	cv.CV_CAP_PROP_CONTRAST: 50,
	cv.CV_CAP_PROP_SATURATION: 50,
}

for param, value in config.iteritems():
	cv.SetCaptureProperty(capture, param, value)


Параметры яркости, контраста и насыщенности задаются в диапазоне от 1 до 100. Их комбинация может значительно улучшить качество съемки в затемненных помещениях.

Несколько примеров:


Кадр с параметрами по умолчанию


Яркость, контраст и насыщенность равны 50 пунктам


Яркость 50, контраст 70, насыщенность 0

Бывает так, что кадры с камеры нужно обрабатывать библиотекой PIL. Чтобы конвертировать их из формата cv в PIL, не обязательно сохранять их на диск, достаточно выполнить код:

pil_img = Image.fromstring("L", cv.GetSize(frame), frame.tostring())


И в обратную сторону:

cv_img = cv.CreateImageHeader(pil_img.size, cv.IPL_DEPTH_8U, 3)
cv.SetData(cv_img, pil_img.tostring())


В итоге мы имеем программный доступ к камере, можем снимать кадры и слать их по почте, делать какой-то анализ, писать видео. Решение кросплатформенно. Думаю, несложно организовать стримминг. Тут, кстати, сама собой напрашивается программка с GUI-интерфейсом на wx, например.

Ссылки:

1) Библиотека OpenCV
2) Документация по биндингу к Питону
3) Раздел «highgui»
Иван Гришаев @igrishaev
карма
98,0
рейтинг 18,5
Пользователь
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (32)

  • НЛО прилетело и опубликовало эту надпись здесь
  • +4
    А почему вы выбрали python? Имхо, для ваших целей подошел бы и С(и не надо ничего устанавливать).
    • +1
      Си не умею )
      • –9
        Я посоветовал бы тебе посмотреть С#, хороший яп (и на его синтаксис повлиял python).
        • +5
          С# на *nix'ах — это не комильфо. С, в некоторых случаях С++, а в данном случае точно бы хватило С. Тем более С очень простой и лежит в основе огромного количества языков.
          • –6
            Спасибо, не знал)Просто в поисках яп после рассмотрения python я перешел на с# и он мне больше понравился поэтому и посоветовал(а про *nix не знал)
    • +3
      Библиотека opencv на python весьма неплохо оптимизирована и официально поддерживается. Да и написать такой простой скрипт на python в разы проще и быстрее, чем на программу С
  • 0
    ТО что она работает без драйверов это нормально. Китайцы что еще тратится на написание драйвером будут?)
    • +1
      Ну, мне попадались те, что требовали драйвера. Счел нужным уточнить.
      • 0
        Хотя чего это я. Китайцы могут покупать железки для вебок, которые продаются вместе с драйверами. Все верно.
  • +1
    Спасибо за примеры. Хочу сделать дома видеонаблюдение avi(хистори)+jpg(для просмотра с телефона) линуксом и EasyCap на 4 канала. еще бы moition detect раскурить и будет приличный DVR…
    • 0
      что мешает использовать програмку motion? пишет, вещает в сеть, умеет собития при старте движений итд…
    • 0
      >>Хочу сделать дома видеонаблюдение avi(хистори)+jpg(для просмотра с телефона) линуксом и EasyCap на 4 канала. еще бы moition detect раскурить и будет приличный DVR…

      Посмотрите сюда:
      www.zoneminder.com/
  • +5
    Под линуксом все же лучше использовать v4l2, возможностей гораздо больше.
    Для сжатия рекомендую посмотреть список кодеков, например, здесь: www.fourcc.org/codecs.php т.к. в документации OpenCV для примера указаны лишь два варианта.
    Сейчас в своем проекте используем v4l2, возможно, позже получится неплохой враппер. А если постараемся, то сделаем даже кроссплатформенную библиотеку(враппер) для захвата видео.
    • 0
      Спасибо большое за ссылку! Добавлю в пост.
    • НЛО прилетело и опубликовало эту надпись здесь
      • +1
        Так говорите, будто в какой-то другой библиотеке все камеры будут поддерживать единый формат.
        Да, вероятно, там сделают удобную обертку, скрыв все от вас. Здесь каждый решает сам, что ему нужно, сел и поехал, или разобрался, написал свой велосипед с дополнительными фичами и оптимизациями и поеахал.
        В общем не считаю, что v4l2 использовать напрямую это гемморой, все ситуации легко обрабатываются.
  • 0
    кстати, не по теме: а кнопка на камере как-то работает?
    • 0
      Возможно, но я еще не понял, как.
  • 0
    Ну зачем микроскопом гвозди-то забивать?!
    Нафига openCV совать туда, где хватит обычного v4l2?
    • 0
      Вы читали статью?

      >> Поскольку я работаю как в Винде, так и в Линуксе, решение должно удовлетворять обоим ОС.

      >> Во-первых, мне было интересно освоить OpenCV, а во-вторых, про использование v4l уже была отличная хабрастатья.
      • 0
        Ну так стоило бы и показать рациональность использования OpenCV: хоть какую-то обработку видео сделать (например, движущиеся объекты искать или мимику/жесты распознавать).
        А лишь ради кроссплатформенности тащить OpenCV == /0
        • 0
          Мне не нужна была обработка.

          Мне была нужна возможность захвата с камеры в такой момент и такой формат, в какой я захочу. Плюс чтобы было кроссплатформенно.

          Вас что-то не устраивает? Предложите свой вариант.
          • 0
            > Предложите свой вариант

            ffmpeg: кроссплатформенно + легче. Ну, а при разработке только под линукс, естественно, v4l2 — самое оно.
            • 0
              > ffmpeg: кроссплатформенно

              Как хватать с камеры через ffmpeg БЕЗ video4linux2?
  • +1
    • 0
      Вот так бы сразу и сказали.
      Спасибо за ссылку, всяческих вам плюсов!
  • 0
    А кто нибудь себе представляет, как написать «сплиттер», который позволит веб камеру «шарить» для использования несколькими клиентами, например skype и gtalk, без заморочки с нативными MFT или DirectShow?
    • 0
      В линуксе это делается через буфер пользователя (v4l): одна программа «грабит» и складывает в общий буфер, а уйма остальных — пользуются. В винде наверняка так тоже можно.
      Еще как вариант — ffserver.
      • 0
        Спасибо, похоже это не прокатит — виндусовские апликации не поймут.
        Ну почему MS обязаны такой огород всегда городить со своими доморощенными API?
        Насколько привык как пользователь, настолько не перношу их как разработчик.
        Прошу прощения за оффтоп — ну наболело.

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