Начинаем реализацию: первые досадные огорчения
Я был очень удивлен и расстроен, когда узнал, что в великом и могучем .NET
Framework напрочь отсутствует возможность простого взаимодействия с веб-камерами.
В четвертой версии ситуация улучшилась (для SilverLight-проектов точно появились
соответствующие классы), но протестировать я их не успел, поскольку пример для
данной статьи я начал писать еще до официального выхода VS2010 и 4-го .NET’a.
Практически отчаявшись, я плотно засел в гугле. Результаты поиска по рунету
меня не вдохновили – все, что я нашел – это ссылки на MSDN и технологию
DirectDraw. Я даже попробовал набросать простенький примерчик, но из-за
отсутствия опыта работы с DirectDraw меня постиг облом. У меня получилось
собрать совсем простенькое приложение, но я так и не смог выловить в нем все
глюки.
Еще больше отчаявшись, я принялся шерстить ресурсы наших западных товарищей.
Проштудировав несколько десятков ссылок, я смог нарыть много вкусностей. Среди
них были всевозможные примеры и небольшие статейки (американцы не любят много
писать). Мне даже удалось найти рабочий пример на основе DirectDraw, но, когда я
увидел код – ужаснулся. Разобраться в нем было тяжело. Поэтому я решил с ним не
заморачиваться, а попытаться найти способ попроще. Не успел я распрощаться с
примером на DirectDraw, как на глаза мне попался еще один. Автор примера закодил
целую библиотеку для работы с веб-камерами и другими устройствами видеозахвата,
используя технологию VFW (Video For Windows).
Жаль, что проект автора (я про библиотеку) был максимально кастрирован. Все,
что позволяла сделать библиотека – вывести изображение с веб-камеры. Ни захвата
отдельных кадров, ни записи видео и других полезных нам фич не было.
И тем не менее, мое подсознание решительно сказало мне, что этот проект и
есть то, что я искал. Не успел я беглым взглядом пробежаться по его коду, как
увидел имена знакомых win-сообщений и не менее знакомых названий WinAPI функций.
Когда-то давным-давно мне приходилось писать приложение для работы с веб-камерой
на Delphi. Тогда я и столкнулся с этими функциями впервые.
Посмотрев сорцы, я решил написать свою версию библиотеки и снабдить ее нужным
функционалом.
Взвод, готовность №1
Вполне возможно, что в одном компе/ноуте может быть несколько веб-камер. За
примером далеко ходить не надо. Мне по работе часто приходится организовывать
простенькие видеоконференции. Обычно в них участвуют два человека. Каждого из
участников снимает отдельная камера. Сами камеры подключены к моему компу. Когда
я начинаю съемку, то выбираю в программе для работы с видеокамерами нужную в
настоящий момент камеру. Раз уж мы решили взять камеру под контроль, то обязаны
разобраться, как получать список установленных устройств видеозахвата и выбрать
то, с которым будем работать в настоящий момент.
Для решения этой нехитрой задачи в WindowsAPI предусмотрена функция
capGetDriverDescription(). Она принимает пять параметров:
есть, теперь посмотрим, как определить ее в C#. Делается это так:
[DllImport("avicap32.dll")]
protected static extern bool capGetDriverDescriptionA (short wDriverIndex, [MarshalAs(UnmanagedType.VBByRefStr)]
ref String lpszName, int cbName, [MarshalAs(UnmanagedType.VBByRefStr)] ref
String lpszVer, int cbVer);
Обрати внимание, что перед тем, как указать имя подключаемой функции, в
обязательном порядке требуется написать имя DLL, в которой она определена. В
нашем случае это avicap32.dll.
Так, функция импортирована, теперь можно написать класс, в котором она будет
использоваться. Весь код класса для получения списка устройств я приводить не
стану, покажу лишь код ключевого метода:
public static Device[] GetAllCapturesDevices()
{
String dName = "".PadRight(100);
String dVersion = "".PadRight(100);
for (short i = 0; i < 10; i++)
{
if (capGetDriverDescriptionA(i,
ref dName, 100, ref dVersion,
100))
{
Device d = new Device(i);
d.Name = dName.Trim();
d.Version = dVersion.Trim();
devices.Add(d);
}
}
return (Device[])devices.ToArray
(typeof(Device));
}
Код выглядит проще некуда. Самое интересное место в нем – цикл, в котором
происходит вызов упомянутой выше функции capGetDriverDescription. Из MSDN мы
знаем, что индекс (первый параметр функции capGetDriverDescription()) может
варьироваться от 0 до 9, поэтому мы целенаправленно запускаем цикл в этом
диапазоне. Результатом выполнения метода будет массив классов Device (этот класс
я определил самостоятельно, смотри соответствующие исходники).
С получением списка устройств разобрались, теперь позаботимся об отображении
видеопотока с камеры. Тут нам сослужит хорошую службу функция
capCreateCaptureWindow(), предназначенная для создания окна захвата.
Немного забегая вперед, скажу, что дальнейшие действия с камерой будут
происходить путем банальной отправки сообщений окну захвата. Да, именно так,
придется воспользоваться до боли знакомой windows-программисту (и приколисту)
функцией SendMessage().
Теперь присмотримся внимательнее к функции capCreateCaptureWindow(). Ей
требуется передать шесть аргументов:
ошибки. Поскольку эта функция также относится к WinAPI, то ее опять-таки нужно
импортировать. Код импортирования приводить не буду, поскольку он практически
идентичен тому, что я писал для функции capGetDriverDescription(). Лучше сразу
взглянем на процесс инициализации камеры:
deviceHandle = capCreateCaptureWindowA (ref deviceIndex, WS_VISIBLE |
WS_CHILD, 0, 0, windowWidth, windowHeight, handle, 0);
if (SendMessage(deviceHandle, WM_CAP_DRIVER_CONNECT, this.index, 0) > 0)
{
SendMessage(deviceHandle, WM_CAP_SET_SCALE, -1, 0);
SendMessage(deviceHandle, WM_CAP_SET_PREVIEWRATE, 0x42, 0);
SendMessage(deviceHandle, WM_CAP_SET_PREVIEW, -1, 0);
SetWindowPos(deviceHandle, 1, 0, 0, windowWidth, windowHeight, 6);
}
В этом коде сразу после создания окна производится попытка отправки сообщения
WM_CAP_DRIVER_CONNECT. Отличный от нуля результат выполнения функции расскажет
нам о ее успешности.
Теперь представим, что сегодня боги на нашей стороне и произведем
незамедлительную отправку нескольких сообщений: WM_CAP_SET_SCALE,
WM_CAP_SET_PREVIEWRATE, WM_CAP_SET_PREVIEW. Увы, как и в случае с функциями, C#
ничего не знает о существовании этих констант. Тебе опять придется определять их
самостоятельно. Список всех необходимых констант с комментариями я привел ниже.
//Пользовательское сообщение
private const int WM_CAP = 0x400;
//Соединение с драйвером устройства видеозахвата
private const int WM_CAP_DRIVER_CONNECT = 0x40a;
//Разрыв связи с драйвером видеозахвата
private const int WM_CAP_DRIVER_DISCONNECT = 0x40b;
//Копирование кадра в буффер обмена
private const int WM_CAP_EDIT_COPY = 0x41e;
//Включение/отключение режима предпосмотра
private const int WM_CAP_SET_PREVIEW = 0x432;
//Включение/отключение режима оверлей
private const int WM_CAP_SET_OVERLAY = 0x433;
//Скорость previewrate
private const int WM_CAP_SET_PREVIEWRATE = 0x434;
//Включение/отключение масштабирования
private const int WM_CAP_SET_SCALE = 0x435;
private const int WS_CHILD = 0x40000000;
private const int WS_VISIBLE = 0x10000000;
//Установка callback-функции для preview
private const int WM_CAP_SET_CALLBACK_FRAME = 0x405;
//Получение одиночного фрейма с драйвера видеозахвата
private const int WM_CAP_GRAB_FRAME = 0x43c;
//Сохранение кадра с камеры в файл
private const int WM_CAP_SAVEDIB = 0x419;
Дальнейшее описание класса для работы с веб-камерой я опущу. Каркас я
рассмотрел, а со всем остальным ты легко разберешься путем раскуривания моего
хорошо прокомментированного исходника. Единственное, что я не хотел бы оставлять
за кадром – это пример использования библиотеки.
Всего в библиотеке я реализовал (точнее, дописал) пару методов: GetAllDevices
(уже рассматривали), GetDevice (получение драйвера устройства видеозахвата по
индексу), ShowWindow (отображение изображения с веб-камеры), GetFrame (захват
отдельного кадра в графический файл) и GetCapture (захват видеопотока).
В качестве демонстрации работоспособности изготовленной либы я набросал
небольшое приложение. На форме я расположил один компонент ComboBox
(используется для хранения списка имеющихся устройств видеозахвата) и несколько
кнопок – "Обновить", "Пуск", "Остановить" и "Скриншот". Ах да, еще на моей форме
пестреет компонент Image. Его я применяю для отображения видео с камеры.
Разбор полетов начнем с кнопки "Обновить". По ее нажатию я получаю список
всех установленных устройств видеозахвата. Начинка этого обработчика события:
Device[] devices = DeviceManager.GetAllDevices();
foreach (Device d in devices)
{
cmbDevices.Items.Add(d);
}
Правда, все просто? Разработанная нами библиотека берет на себя все черную
работу и нам остается лишь наслаждаться объектно-ориентированным
программированием. Еще проще выглядит код для включения отображения видеопотока
с камеры:
Device selectedDevice = DeviceManager.GetDevice(cmbDevices.SelectedIndex);
selectedDevice.ShowWindow(this.picCapture);
Опять же, все проще пареной репы. Ну и теперь взглянем на код кнопки "Скриншот":
Device selectedDevice = DeviceManager.GetDevice(cmbDevices.SelectedIndex);
selectedDevice.FrameGrabber();
Я не стал уделять особого внимания методу FrameGrabber(). В моем исходнике
вызов метода приводит к сохранению текущего кадра прямо в корень системного
диска. Разумеется, это не очень корректно, поэтому перед боевым применением
программы не забудь внести все необходимые поправки.
Готовность № 3
Теперь настало время поговорить о том, как соорудить простенькую, но надежную
систему видеонаблюдения. Обычно такие системы базируются на двух алгоритмах:
различие двух фреймов и простое моделирование фона. Их реализация (код)
достаточно объемна, поэтому в самый последний момент я решил пойти по более
простому пути. Под легким путем подразумевается использование мощного, но пока
малоизвестного фреймворка для .NET – AForge.NET.
AForge.NET в первую очередь предназначен для разработчиков и исследователей.
С его помощью, девелоперы могут существенно облегчить свой труд при разработке
проектов для следующих областей: нейросети, работа с изображениями (наложение
фильтров, редактирование изображений, попиксельная фильтрация, изменение
размера, поворот изображения), генетика, робототехника, взаимодействие с видео
устройствами и т.д. С фреймворком поставляется хорошая документация. В ней
описаны абсолютно все возможности продукта. Не поленись хорошенько с ней
ознакомиться. Особенно мне хочется отметить качество кода этого продукта. Все
написано цивильно и копаться в коде – одно удовольствие.
Теперь вернемся к нашей непосредственной задаче. Скажу честно, средствами
фреймворка она решается как дважды два. "Тогда зачем ты мне парил мозг WinAPI
функциями?" – недовольно спросишь ты. А за тем, чтобы ты не был ни в чем
ограничен. Сам ведь знаешь, что проекты бывают разные. Где-то удобнее применить
махину .NET, а где-то проще обойтись старым добрым WinAPI.
Вернемся к нашей задачке. Для реализации детектора движений нам придется
воспользоваться классом MotionDetector из вышеупомянутого фреймворка. Класс
отлично оперирует объектами типа Bitmap и позволяет быстренько вычислить процент
расхождения между двумя изображениями. В виде кода это будет выглядеть примерно
так:
MotionDetector detector = new MotionDetector(
new TwoFramesDifferenceDetector( ),
new MotionAreaHighlighting( ) );
//Обработка очередного кадра
if ( detector != null )
{
float motionLevel = detector.ProcessFrame( image );
if ( motionLevel > motionAlarmLevel )
{
flash = (int) ( 2 * ( 1000 / alarmTimer.Interval ) );
}
if ( detector.MotionProcessingAlgorithm is BlobCountingObjectsProcessing )
{
BlobCountingObjectsProcessing countingDetector = (BlobCountingObjectsProcessing)
detector.MotionProcessingAlgorithm;
objectsCountLabel.Text = "Objects: " + countingDetector.ObjectsCount.ToString(
);
}
else
{
objectsCountLabel.Text = "";
}
}
}
Вышеприведенный код (не считая инициализацию класса MotionDetector) у меня
выполняется при получении очередного кадра с веб-камеры. Получив кадр, я
выполняю банальное сравнение (метод ProcessFrame): если значение переменной
motionlevel больше motionLevelAlarm (0.015f), то значит, надо бить тревогу!
Движение обнаружено. На одном из скришотов хорошо видна работа демонстрация
детектора движений.
Готовность №4
Веб-камеру можно запросто приспособить для распознавания лиц и создания
продвинутого способа лог-она в систему? Если переварив весь этот материал, ты
думаешь, что это сложно, то ты ошибаешься! В конце марта на сайте
http://codeplex.com (хостинг
для OpenSource проектов от MS) появился пример (а затем и ссылка на статью),
демонстрирующий реализацию программы для распознавания лиц с использованием
веб-камеры. Сам пример основан на использовании новых возможностей .NET и
SilverLight. Разобрать этот пример в рамках журнальной статьи нереально, так как
автор исходника постарался и сделал все максимально шикарно. Тут тебе и
алгоритмы для работы с изображениями (фильтр размытия, уменьшения шума,
попиксельное сравнение, растяжка и т.д.) и демонстрация новинок SilverLight и
много чего еще. Одним словом, must use! Ссылку на проект и статью ищи ниже.
Конец фильма
Приведенные в статье примеры послужат тебе хорошей отправной точкой. На их
основе легко сварганить как профессиональную утилиту для работы с веб-камерой, и
поднимать на ее продаже несколько сотен баксов в квартал или написать хитрого и
злобного трояна-шпиона.
Вспомни статью про бэкап
Skype-бесед. В ней я говорил, что времена клавиатурных шпионов уже прошли.
Сейчас особенно актуальны аудио и видеоданные. Если учесть, что сегодня
веб-камера – обязательный атрибут любого ноутбука, то нетрудно представить,
сколько интересного видео ты сможешь заснять, подсунув жертве "полезную
программку"… Однако я тебе этого не говорил :). Удачи в программировании, а
будут вопросы – пиши.
WWW
http://blogs.msdn.com/
– Русская версия статьи "Silverlight 4 real-time Face Detection"
(распознавание лиц в реальном времени при помощи SilverLight).
http://facelight.codeplex.com/ – здесь хостится проект "Facelight",
позволяющий распознавать лица в реальном времени. Если ты собрался закодить
серьезную софтину для определения лиц или логона в систему, то посмотреть на
этот проект просто обязан.
http://www.aforgenet.com/framework/ – тут ты
найдешь AForge .NET – отличный и простой в использовании фреймворк для работы с
видео, изображениями и т.д.
http://vr-online.ru
– все исходники примеров, а также кучу дополнительной информации ты
можешь слить с сайта проекта VR-Online.
Я был очень удивлен и расстроен, когда узнал, что в великом и могучем .NET
Framework напрочь отсутствует возможность простого взаимодействия с веб-камерами.
В четвертой версии ситуация улучшилась (для SilverLight-проектов точно появились
соответствующие классы), но протестировать я их не успел, поскольку пример для
данной статьи я начал писать еще до официального выхода VS2010 и 4-го .NET’a.
Практически отчаявшись, я плотно засел в гугле. Результаты поиска по рунету
меня не вдохновили – все, что я нашел – это ссылки на MSDN и технологию
DirectDraw. Я даже попробовал набросать простенький примерчик, но из-за
отсутствия опыта работы с DirectDraw меня постиг облом. У меня получилось
собрать совсем простенькое приложение, но я так и не смог выловить в нем все
глюки.
Еще больше отчаявшись, я принялся шерстить ресурсы наших западных товарищей.
Проштудировав несколько десятков ссылок, я смог нарыть много вкусностей. Среди
них были всевозможные примеры и небольшие статейки (американцы не любят много
писать). Мне даже удалось найти рабочий пример на основе DirectDraw, но, когда я
увидел код – ужаснулся. Разобраться в нем было тяжело. Поэтому я решил с ним не
заморачиваться, а попытаться найти способ попроще. Не успел я распрощаться с
примером на DirectDraw, как на глаза мне попался еще один. Автор примера закодил
целую библиотеку для работы с веб-камерами и другими устройствами видеозахвата,
используя технологию VFW (Video For Windows).
Жаль, что проект автора (я про библиотеку) был максимально кастрирован. Все,
что позволяла сделать библиотека – вывести изображение с веб-камеры. Ни захвата
отдельных кадров, ни записи видео и других полезных нам фич не было.
И тем не менее, мое подсознание решительно сказало мне, что этот проект и
есть то, что я искал. Не успел я беглым взглядом пробежаться по его коду, как
увидел имена знакомых win-сообщений и не менее знакомых названий WinAPI функций.
Когда-то давным-давно мне приходилось писать приложение для работы с веб-камерой
на Delphi. Тогда я и столкнулся с этими функциями впервые.
Посмотрев сорцы, я решил написать свою версию библиотеки и снабдить ее нужным
функционалом.
Взвод, готовность №1
Вполне возможно, что в одном компе/ноуте может быть несколько веб-камер. За
примером далеко ходить не надо. Мне по работе часто приходится организовывать
простенькие видеоконференции. Обычно в них участвуют два человека. Каждого из
участников снимает отдельная камера. Сами камеры подключены к моему компу. Когда
я начинаю съемку, то выбираю в программе для работы с видеокамерами нужную в
настоящий момент камеру. Раз уж мы решили взять камеру под контроль, то обязаны
разобраться, как получать список установленных устройств видеозахвата и выбрать
то, с которым будем работать в настоящий момент.
Для решения этой нехитрой задачи в WindowsAPI предусмотрена функция
capGetDriverDescription(). Она принимает пять параметров:
- wDriverIndex – индекс драйвера видеозахвата. Значение индекса может
- варьироваться от 0 до 9;
- lpszName – указатель на буфер, содержащий соответствующее имя драйвера;
- cbName – размер (в байтах) буфера lpszName;
- lpszVer – указатель на буфер, содержащий описание определенного
- драйвера;
- cbVer – размер буфера (в байтах), в котором хранится описание драйвера.
есть, теперь посмотрим, как определить ее в C#. Делается это так:
[DllImport("avicap32.dll")]
protected static extern bool capGetDriverDescriptionA (short wDriverIndex, [MarshalAs(UnmanagedType.VBByRefStr)]
ref String lpszName, int cbName, [MarshalAs(UnmanagedType.VBByRefStr)] ref
String lpszVer, int cbVer);
Обрати внимание, что перед тем, как указать имя подключаемой функции, в
обязательном порядке требуется написать имя DLL, в которой она определена. В
нашем случае это avicap32.dll.
Так, функция импортирована, теперь можно написать класс, в котором она будет
использоваться. Весь код класса для получения списка устройств я приводить не
стану, покажу лишь код ключевого метода:
public static Device[] GetAllCapturesDevices()
{
String dName = "".PadRight(100);
String dVersion = "".PadRight(100);
for (short i = 0; i < 10; i++)
{
if (capGetDriverDescriptionA(i,
ref dName, 100, ref dVersion,
100))
{
Device d = new Device(i);
d.Name = dName.Trim();
d.Version = dVersion.Trim();
devices.Add(d);
}
}
return (Device[])devices.ToArray
(typeof(Device));
}
Код выглядит проще некуда. Самое интересное место в нем – цикл, в котором
происходит вызов упомянутой выше функции capGetDriverDescription. Из MSDN мы
знаем, что индекс (первый параметр функции capGetDriverDescription()) может
варьироваться от 0 до 9, поэтому мы целенаправленно запускаем цикл в этом
диапазоне. Результатом выполнения метода будет массив классов Device (этот класс
я определил самостоятельно, смотри соответствующие исходники).
С получением списка устройств разобрались, теперь позаботимся об отображении
видеопотока с камеры. Тут нам сослужит хорошую службу функция
capCreateCaptureWindow(), предназначенная для создания окна захвата.
Немного забегая вперед, скажу, что дальнейшие действия с камерой будут
происходить путем банальной отправки сообщений окну захвата. Да, именно так,
придется воспользоваться до боли знакомой windows-программисту (и приколисту)
функцией SendMessage().
Теперь присмотримся внимательнее к функции capCreateCaptureWindow(). Ей
требуется передать шесть аргументов:
- lpszWindowName – нуль-терминальная строка, содержащая имя окна захвата;
- dwStyle – стиль окна;
- x – координата X;
- y – координата Y;
- nWidth – ширина окна;
- nHeight – высота окна;
- hWnd – handle родительского окна;
- nID – идентификатор окна.
ошибки. Поскольку эта функция также относится к WinAPI, то ее опять-таки нужно
импортировать. Код импортирования приводить не буду, поскольку он практически
идентичен тому, что я писал для функции capGetDriverDescription(). Лучше сразу
взглянем на процесс инициализации камеры:
deviceHandle = capCreateCaptureWindowA (ref deviceIndex, WS_VISIBLE |
WS_CHILD, 0, 0, windowWidth, windowHeight, handle, 0);
if (SendMessage(deviceHandle, WM_CAP_DRIVER_CONNECT, this.index, 0) > 0)
{
SendMessage(deviceHandle, WM_CAP_SET_SCALE, -1, 0);
SendMessage(deviceHandle, WM_CAP_SET_PREVIEWRATE, 0x42, 0);
SendMessage(deviceHandle, WM_CAP_SET_PREVIEW, -1, 0);
SetWindowPos(deviceHandle, 1, 0, 0, windowWidth, windowHeight, 6);
}
В этом коде сразу после создания окна производится попытка отправки сообщения
WM_CAP_DRIVER_CONNECT. Отличный от нуля результат выполнения функции расскажет
нам о ее успешности.
Теперь представим, что сегодня боги на нашей стороне и произведем
незамедлительную отправку нескольких сообщений: WM_CAP_SET_SCALE,
WM_CAP_SET_PREVIEWRATE, WM_CAP_SET_PREVIEW. Увы, как и в случае с функциями, C#
ничего не знает о существовании этих констант. Тебе опять придется определять их
самостоятельно. Список всех необходимых констант с комментариями я привел ниже.
//Пользовательское сообщение
private const int WM_CAP = 0x400;
//Соединение с драйвером устройства видеозахвата
private const int WM_CAP_DRIVER_CONNECT = 0x40a;
//Разрыв связи с драйвером видеозахвата
private const int WM_CAP_DRIVER_DISCONNECT = 0x40b;
//Копирование кадра в буффер обмена
private const int WM_CAP_EDIT_COPY = 0x41e;
//Включение/отключение режима предпосмотра
private const int WM_CAP_SET_PREVIEW = 0x432;
//Включение/отключение режима оверлей
private const int WM_CAP_SET_OVERLAY = 0x433;
//Скорость previewrate
private const int WM_CAP_SET_PREVIEWRATE = 0x434;
//Включение/отключение масштабирования
private const int WM_CAP_SET_SCALE = 0x435;
private const int WS_CHILD = 0x40000000;
private const int WS_VISIBLE = 0x10000000;
//Установка callback-функции для preview
private const int WM_CAP_SET_CALLBACK_FRAME = 0x405;
//Получение одиночного фрейма с драйвера видеозахвата
private const int WM_CAP_GRAB_FRAME = 0x43c;
//Сохранение кадра с камеры в файл
private const int WM_CAP_SAVEDIB = 0x419;
Дальнейшее описание класса для работы с веб-камерой я опущу. Каркас я
рассмотрел, а со всем остальным ты легко разберешься путем раскуривания моего
хорошо прокомментированного исходника. Единственное, что я не хотел бы оставлять
за кадром – это пример использования библиотеки.
Всего в библиотеке я реализовал (точнее, дописал) пару методов: GetAllDevices
(уже рассматривали), GetDevice (получение драйвера устройства видеозахвата по
индексу), ShowWindow (отображение изображения с веб-камеры), GetFrame (захват
отдельного кадра в графический файл) и GetCapture (захват видеопотока).
В качестве демонстрации работоспособности изготовленной либы я набросал
небольшое приложение. На форме я расположил один компонент ComboBox
(используется для хранения списка имеющихся устройств видеозахвата) и несколько
кнопок – "Обновить", "Пуск", "Остановить" и "Скриншот". Ах да, еще на моей форме
пестреет компонент Image. Его я применяю для отображения видео с камеры.
Разбор полетов начнем с кнопки "Обновить". По ее нажатию я получаю список
всех установленных устройств видеозахвата. Начинка этого обработчика события:
Device[] devices = DeviceManager.GetAllDevices();
foreach (Device d in devices)
{
cmbDevices.Items.Add(d);
}
Правда, все просто? Разработанная нами библиотека берет на себя все черную
работу и нам остается лишь наслаждаться объектно-ориентированным
программированием. Еще проще выглядит код для включения отображения видеопотока
с камеры:
Device selectedDevice = DeviceManager.GetDevice(cmbDevices.SelectedIndex);
selectedDevice.ShowWindow(this.picCapture);
Опять же, все проще пареной репы. Ну и теперь взглянем на код кнопки "Скриншот":
Device selectedDevice = DeviceManager.GetDevice(cmbDevices.SelectedIndex);
selectedDevice.FrameGrabber();
Я не стал уделять особого внимания методу FrameGrabber(). В моем исходнике
вызов метода приводит к сохранению текущего кадра прямо в корень системного
диска. Разумеется, это не очень корректно, поэтому перед боевым применением
программы не забудь внести все необходимые поправки.
Готовность № 3
Теперь настало время поговорить о том, как соорудить простенькую, но надежную
систему видеонаблюдения. Обычно такие системы базируются на двух алгоритмах:
различие двух фреймов и простое моделирование фона. Их реализация (код)
достаточно объемна, поэтому в самый последний момент я решил пойти по более
простому пути. Под легким путем подразумевается использование мощного, но пока
малоизвестного фреймворка для .NET – AForge.NET.
AForge.NET в первую очередь предназначен для разработчиков и исследователей.
С его помощью, девелоперы могут существенно облегчить свой труд при разработке
проектов для следующих областей: нейросети, работа с изображениями (наложение
фильтров, редактирование изображений, попиксельная фильтрация, изменение
размера, поворот изображения), генетика, робототехника, взаимодействие с видео
устройствами и т.д. С фреймворком поставляется хорошая документация. В ней
описаны абсолютно все возможности продукта. Не поленись хорошенько с ней
ознакомиться. Особенно мне хочется отметить качество кода этого продукта. Все
написано цивильно и копаться в коде – одно удовольствие.
Теперь вернемся к нашей непосредственной задаче. Скажу честно, средствами
фреймворка она решается как дважды два. "Тогда зачем ты мне парил мозг WinAPI
функциями?" – недовольно спросишь ты. А за тем, чтобы ты не был ни в чем
ограничен. Сам ведь знаешь, что проекты бывают разные. Где-то удобнее применить
махину .NET, а где-то проще обойтись старым добрым WinAPI.
Вернемся к нашей задачке. Для реализации детектора движений нам придется
воспользоваться классом MotionDetector из вышеупомянутого фреймворка. Класс
отлично оперирует объектами типа Bitmap и позволяет быстренько вычислить процент
расхождения между двумя изображениями. В виде кода это будет выглядеть примерно
так:
MotionDetector detector = new MotionDetector(
new TwoFramesDifferenceDetector( ),
new MotionAreaHighlighting( ) );
//Обработка очередного кадра
if ( detector != null )
{
float motionLevel = detector.ProcessFrame( image );
if ( motionLevel > motionAlarmLevel )
{
flash = (int) ( 2 * ( 1000 / alarmTimer.Interval ) );
}
if ( detector.MotionProcessingAlgorithm is BlobCountingObjectsProcessing )
{
BlobCountingObjectsProcessing countingDetector = (BlobCountingObjectsProcessing)
detector.MotionProcessingAlgorithm;
objectsCountLabel.Text = "Objects: " + countingDetector.ObjectsCount.ToString(
);
}
else
{
objectsCountLabel.Text = "";
}
}
}
Вышеприведенный код (не считая инициализацию класса MotionDetector) у меня
выполняется при получении очередного кадра с веб-камеры. Получив кадр, я
выполняю банальное сравнение (метод ProcessFrame): если значение переменной
motionlevel больше motionLevelAlarm (0.015f), то значит, надо бить тревогу!
Движение обнаружено. На одном из скришотов хорошо видна работа демонстрация
детектора движений.
Готовность №4
Веб-камеру можно запросто приспособить для распознавания лиц и создания
продвинутого способа лог-она в систему? Если переварив весь этот материал, ты
думаешь, что это сложно, то ты ошибаешься! В конце марта на сайте
http://codeplex.com (хостинг
для OpenSource проектов от MS) появился пример (а затем и ссылка на статью),
демонстрирующий реализацию программы для распознавания лиц с использованием
веб-камеры. Сам пример основан на использовании новых возможностей .NET и
SilverLight. Разобрать этот пример в рамках журнальной статьи нереально, так как
автор исходника постарался и сделал все максимально шикарно. Тут тебе и
алгоритмы для работы с изображениями (фильтр размытия, уменьшения шума,
попиксельное сравнение, растяжка и т.д.) и демонстрация новинок SilverLight и
много чего еще. Одним словом, must use! Ссылку на проект и статью ищи ниже.
Конец фильма
Приведенные в статье примеры послужат тебе хорошей отправной точкой. На их
основе легко сварганить как профессиональную утилиту для работы с веб-камерой, и
поднимать на ее продаже несколько сотен баксов в квартал или написать хитрого и
злобного трояна-шпиона.
Вспомни статью про бэкап
Skype-бесед. В ней я говорил, что времена клавиатурных шпионов уже прошли.
Сейчас особенно актуальны аудио и видеоданные. Если учесть, что сегодня
веб-камера – обязательный атрибут любого ноутбука, то нетрудно представить,
сколько интересного видео ты сможешь заснять, подсунув жертве "полезную
программку"… Однако я тебе этого не говорил :). Удачи в программировании, а
будут вопросы – пиши.
WWW
http://blogs.msdn.com/
– Русская версия статьи "Silverlight 4 real-time Face Detection"
(распознавание лиц в реальном времени при помощи SilverLight).
http://facelight.codeplex.com/ – здесь хостится проект "Facelight",
позволяющий распознавать лица в реальном времени. Если ты собрался закодить
серьезную софтину для определения лиц или логона в систему, то посмотреть на
этот проект просто обязан.
http://www.aforgenet.com/framework/ – тут ты
найдешь AForge .NET – отличный и простой в использовании фреймворк для работы с
видео, изображениями и т.д.
http://vr-online.ru
– все исходники примеров, а также кучу дополнительной информации ты
можешь слить с сайта проекта VR-Online.