Автор: Андрей Шкрыль
Сайт автора: www.vr-online.ru
Опубликовано: 09.01.2008
Первым делом
Итак, нам потребуется web-камера. Для этой статьи я специально приобрел Genius VideoCam Look. Конечно, не самый лучший выбор, но по соотношению "цена/качество", достаточно оптимальный.
После установки дров для нового девайса можно приступать к реализации самой системы контроля. Но для начала придется скачать DSPack - это надстройка над DirectShow и DirectX, состоящая из набора компонентов, позволяющих работать с потоками мультимедиа, в том числе и с устройствами видеозахвата (web-камера, ТВ-тюнер и т.д.). DSPack есть на этом сайте и на официальном сайте пакета: www.progdigy.com. Она абсолютно бесплатна и распространяется по лицензии MPL1.1
Установка DSPack - дело пары минут. Распакуй архив, далее выбери Delphi и укажи, что подключаемые модули твоего проекта нужно искать в каталогах \src\Directx9 и \src\DSPack. Теперь откомпилируй пакеты (каталог packages) DirectX9_Dx.dpk и DSPack_Dx.dpk, где x - номер используемой тобой версии Delphi. В качестве финального аккорда отработанным движением установи пакет DSPackDesign_Dx.dpk.
Проектируем главную форму
Создадим новый проект. Расположим на форме компонент TFilterGraph, назовем его FilterGraph. Свойства Mode компонента обязательно нужно установить в gmCapture, ведь мы будем работать с захватом видео.
Теперь немного теории. Основное понятие DirectShow, а как ты помнишь, DSPack базируется именно на нем, - это "граф фильтров" (странное название, но с этим ничего не поделаешь), который включает набор элементов (фильтров), соединенных в определенном порядке и характеризующих источник аудио- и/или видеопотока, а также способ, которым он обрабатывается (например, декодирование потоковых данных). На рисунке ты можешь видеть пример графа фильтров, предназначенного для воспроизведения mp3-файла.
Далее нам понадобится TVideoWindow - компонент, который используется для отображения картинки с устройства видеозахвата. В его свойстве FilterGraph установим значение FilterGraph. Обрати внимание на свойство FullScreen этого компонента, если его поставить в True, то видео будет транслироваться на весь экран. Теперь разместим на форме TFilter (компонент для управления фильтром), именно для него мы установим web-камеру в качестве источника видеосигнала. Последним нужным нам компонентом из набора будет TSampleGrabber. С помощью него осуществится захват видеопотока, в его свойство FitlerGraph необходимо установить значение FilterGraph. И последний штрих: разместим TComboBox и рядом с ним - метку, содержащую текст: "Выбери устройство". Главную форму будущей программы ты можешь увидеть на рисунке.
Займемся этим
Итак, пора подвинуть клавиатуру поближе и приняться за написание кода нашей будущей мегасофтины. Объявим глобальную переменную:
VideoDevice: TSysDevEnum;
Через нее мы получим список всех устройств (фильтров) видеозахвата, присутствующих в системе. Теперь подключим модули DSUtil и DirectShow9 и для события OnCreate формы напишем следующий обработчик:
Определение устройств видеозахвата
procedure TForm1.FormCreate(Sender: TObject);
var
i: integer;
begin
VideoDevice:= TSysDevEnum.Create(CLSID_VideoInputDeviceCategory);
if VideoDevice.CountFilters > 0 then
for i := 0 to VideoDevice.CountFilters - 1 do
ComboBox1.Items.Add(VideoDevice.Filters[i].FriendlyName);
end;
Первым делом здесь произойдет инициализация нужного нам интерфейса - CLSID_VideoInputDeviceCategory, описанного в модуле DirectShow9. Затем мы формируем список девайсов в TComboBox. Стоит отметить, что если ты поменяешь первую строчку обработчика на «VideoDevice:= TSysDevEnum.Create(CLSID_VideoCompressorCategory);», то в TComboBox появится список кодеков, установленных в системе. А вот таким способом мы получим описание устройств обработки звука:
AudioDevice:= TSysDevEnum.Create(CLSID_CWaveinClassManager);
Идем дальше. Нам потребуется обработчик OnChange для компонента TComboBox. Таким образом, после того как пользователь выберет, откуда он хочет получать видео, оно начнет транслироваться.
Обращение к устройству видеозахвата
procedure TForm1.ComboBox1Change(Sender: TObject);
begin
FilterGraph.ClearGraph;
FilterGraph.Active := false;
//Задаем устройство, с которым будем работать
Filter1.BaseFilter.Moniker := VideoDevice.GetMoniker(ComboBox1.ItemIndex);
FilterGraph.Active := true;
//Задаем, что откуда будем получать и куда оно должно выводиться
with FilterGraph as ICaptureGraphBuilder2 do
RenderStream(@PIN_CATEGORY_PREVIEW, nil, Filter1 as IBaseFilter, SampleGrabber1 as IBaseFilter, VideoWindow1 as IbaseFilter);
//Производим вывод изображения
FilterGraph.Play;
end;
В этом участке кода мы вызываем метод GetMoniker() объекта VideoDevice, для того чтобы установить устройство захвата (в DirectShow используется термин «фильтр»). GetMoniker() описан в модуле DSUtil следующим образом:
function TSysDevEnum.GetMoniker(index: integer): IMoniker;
В качестве результата он возвращает интерфейс IMoniker. Далее происходит активизация графа фильтров (компонент TFilterGraph) и вызывается метод RenderStream(), принадлежащий интерфейсу ICaptureGraphBuilder2. Посредством него мы определяем, какой видеопоток мы хотим обрабатывать и куда он будет выводиться. Описание метода представлено ниже:
Интерфейс IcaptureGraphBuilder2 и входящий в него метод RenderStream()
ICaptureGraphBuilder2 = interface(IUnknown)
['{93E5A4E0-2D50-11d2-ABFA-00A0C9C6E38D}']
(*** ICaptureGraphBuilder2 methods ***)
function SetFiltergraph(pfg: IGraphBuilder): HResult; stdcall;
...
...
...
function RenderStream(pCategory, pType: PGUID; pSource: IUnknown; pfCompressor, pfRenderer: IBaseFilter): HResult; stdcall;
И последнее, что мы делаем, - это начинаем показ видео с помощью метода Play() компонента TFilterGraph. Размещаем на форме кнопку «Остановить видео», даем ей имя ButtonStopPlay, делаем ее недоступной, а в ее обработчике пишем следующий код:
Остановка/возобновление трансляции видео
procedure TForm1.ButtonStopPlayClick(Sender: TObject);
begin
if ButtonStopPlay.Caption='Смотреть видео' then
begin
FilterGraph.Play;
ButtonStopPlay.Caption:='Остановить видео';
end
else
begin
FilterGraph.Stop;
ButtonStopPlay.Caption:='Смотреть видео';
end;
end;
Теперь в обработчике TComboBox последней строчкой добавим:
ButtonStopPlay.Enabled:=True;
Запустим программу и с гордостью посмотрим на то, что у нас получилось (смотри рисунок).
Контролируем периметр
Разместим на форме TImage и кнопку «Сделать скриншот», для которой будет задан следующий обработчик:
SampleGrabber1.GetBitmap(Image1.Picture.Bitmap);
Теперь дело за малым: через равные промежутки времени мы будем получать две картинки, отделенные небольшим интервалом, и проверять, разные ли они. Определим еще один TImage рядом с первым, а под ним - TCheckBox с текстом: «Контроль периметра». Еще потребуется TTimer со свойством Enabled, равным False, и свойством Interval, равным 5000 (5 секунд). Для TCheckBox мы напишем вот такой код:
if CheckBox1.Checked then Timer1.Enabled:=true
else Timer1.Enabled:=false;
Для TTimer создадим обработчик, в котором две картинки будут сравниваться попиксельно и будет считаться количество различий. Если ты теперь запустишь программу, то тебя ждет большой сюрприз. Наведи web-камеру на обычную стену и активизируй функцию контроля периметра. Тебе покажется, что две картинки стены, полученные через разные промежутки времени, практически идентичны, а вот по версии программы они будут отличаться более чем на 60%. Странно, но факт. Честно сказать, меня это очень удивило. Выходом из ситуации здесь является проверка каждого пикселя по системе RGB, то есть перевод цвета из TColor в три составляющие - Red, Green, Blue - и сравнение каждой из них по отдельности для обеих картинок. Если изменения незначительны, можно считать, что их нет вовсе. Код для обработчика TTimer в последнем случае представлен во врезке.
Последнее, что тебе потребуется сделать, - это создать в каталоге программы подкаталог Log, в котором будут сохраняться все подозрительные движения по периметру твоего рабочего стола. Запускай программу и наслаждайся результатом.
Послесловие
Высокими технологиями сейчас мало кого удивишь, хотя при желании это по силам даже обычному программисту. На днях, копаясь в интернете, я нашел отличную библиотеку, позволяющую по фото человека или даже в режиме реального времени по видео с web-камеры определить, кто он. Сразу вспомнились шпионские боевики, и перспектива сотворить нечто подобное на своем домашнем компе, считавшаяся ранее чем-то невозможным и фантастичным, показалась очень заманчивой. Тебе интересно? Тогда посети www.neurotechnologija.com и обязательно загляни в раздел Download. Неплохое продолжение темы работы с web-камерой, не правда ли? А еще это отличная фишка, которой можно удивить коллег по работе или даже босса (по крайне мере их внимание в течении дня тебе обеспечено). Для эксперимента можешь скачать программу VeriLookalgorithmdemo (www.neurotechnologija.com/download/vlook.zip), в ней все элементарно: пункт меню «оооо\аааааа» сохраняет фото человека в базе, а пункт «аааа\аааа» сравнивает нужную тебе картинку с эталонными изображениями, полученными на предыдущем шаге.
Функция контроля периметра
procedure TForm1.Timer1Timer(Sender: TObject);
var
//i-координата пикселя по горизонтали
i:integer;
//j-координата пикселя по вертикали
j:integer;
//Количество различий
k:integer;
r1,g1,b1:Byte;
r2,g2,b2:Byte;
FirstColor,SecondColor:Integer;
Color:TColor;
PriznakChange:byte;
begin
//Делаем первый снимок
if Timer1.Tag=0 then
begin
SampleGrabber1.GetBitmap(Image1.Picture.Bitmap);
Timer1.Tag:=1;
exit;
end;
//Через некоторое время - второй, с которым будем сверять
SampleGrabber1.GetBitmap(Image2.Picture.Bitmap);
Timer1.Tag:=0;
k:=0;
//Начинаем попиксельное сравнение
for i := 1 to Image1.Picture.Bitmap.Height do
begin
for j := 1 to Image1.Picture.Bitmap.Width do
begin
PriznakChange:=0;
//Получаем цвет текущего пикселя первой картинки
FirstColor:=Image1.Picture.Bitmap.Canvas.Pixels[i,j];
//Получаем составляющие RGB
r1:=GetRValue(FirstColor);
g1:=GetGValue(FirstColor);
b1:=GetBValue(FirstColor);
SecondColor:=Image2.Picture.Bitmap.Canvas.Pixels[i,j];
r2:=GetRValue(SecondColor);
g2:=GetGValue(SecondColor);
b2:=GetBValue(SecondColor);
//Начинаем проверку различий между двумя картинками
if Abs(r1-r2)>20 then inc(PriznakChange);
if Abs(g1-g2)>20 then inc(PriznakChange);
if Abs(b1-b2)>20 then inc(PriznakChange);
//Если изменения существенные, то увеличиваем счетчик
if PriznakChange=3 then k:=k+1;
Application.ProcessMessages;
end;
end;
//Если изменений больше 2000
if k>2000 then
begin
Memo1.Lines.Add(FormatDateTime('hh:nn:ss',Now)+' зафиксированы изменения по периметру');
Image2.Picture.Bitmap.SaveToFile('log\'+FormatDateTime('hhnnss',Now)+'.bmp');
end;
end;
Экскурс в историю
История web-камеры берет свое начало в компьютерной лаборатории Кембриджа в 90-х годах XX века. Группа ученых лаборатории работала над проектом в области высоких технологий, и все бы хорошо, да вот только кофеварка у них была одна и располагалась она этажом выше. Таким образом, часто возникала ситуация, когда сотрудник, жаждущий насладиться ароматным напитком, сталкивался с банальным фактом отсутствия кофе, что наверняка его порядком расстраивало, так как бегать с этажа на этаж не самое приятное и полезное времяпрепровождение для ученого. Разрешить ситуацию удалось очень просто: один из компьютеров имел камеру, которую и направили на так необходимую всем кофеварку. В результате желающие знать, есть ли смысл идти за кофе, запускали на своем компьютере специальное ПО, позволяющее получать картинку с камеры, и решали, стоит ли им подниматься или нет.


