Статьи‎ > ‎

Создание детектора движения

Автор: Андрей Шкрыль
Сайт автора: 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 века. Группа ученых лаборатории работала над проектом в области высоких технологий, и все бы хорошо, да вот только кофеварка у них была одна и располагалась она этажом выше. Таким образом, часто возникала ситуация, когда сотрудник, жаждущий насладиться ароматным напитком, сталкивался с банальным фактом отсутствия кофе, что наверняка его порядком расстраивало, так как бегать с этажа на этаж не самое приятное и полезное времяпрепровождение для ученого. Разрешить ситуацию удалось очень просто: один из компьютеров имел камеру, которую и направили на так необходимую всем кофеварку. В результате желающие знать, есть ли смысл идти за кофе, запускали на своем компьютере специальное ПО, позволяющее получать картинку с камеры, и решали, стоит ли им подниматься или нет.

Исходники

Скачать исходники к статье

Comments