Как стать автором
Обновить

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

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


Итак, есть у нас вебкамера и Питон. Прежде всего, нельзя не похвалить камеру, которую я приобрел в 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»
Теги:
Хабы:
+39
Комментарии 32
Комментарии Комментарии 32

Публикации

Истории

Работа

Data Scientist
66 вакансий
Python разработчик
130 вакансий

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн