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

в 6:26, , рубрики: python, вебкамера, видео, захват, обработка изображений, метки: , , ,

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

Итак, есть у нас вебкамера и Питон. Прежде всего, нельзя не похвалить камеру, которую я приобрел в 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 для записи без сжатия (размер файла получится ого-го!).

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

* - обязательные к заполнению поля