Министерство образования и
науки РФ
ФГБОУ ВО
ДАГЕСТАНСКИЙ
ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ
Кафедра
дискретной математики и информатики
А. М.
Магомедов
ВЕБ-КАМЕРЫ В ПРОЕКТАХ DELPHI
МАХАЧКАЛА
– 2017
А. М. Магомедов
ВЕБ-КАМЕРЫ В ПРОЕКТАХ DELPHI
учебно-методическое пособие по теме «Веб-камеры в
проектах Delphi» для студентов
факультета математики и компьютерных наук, обучающихся по специальности
«Фундаментальная информатика и информационные
технологии»
МАХАЧКАЛА – 2017
УДК 681.3.06
Магомедов А. М.
Веб-камеры в проектах в Delphi. – Махачкала,
2017. – 38 с.
В учебно-методическом
пособии излагаются основы создания в среде Delphi приложений, интегрированных с веб-камерами. Пособие рассчитано на
начинающих программистов и, в частности, может быть полезно при написании
курсовых работ по дисциплине «Технологии мультимедиа».
© Магомедов А. М., 2017
Оглавление
Часть 1. Организация программного доступа к веб-камере
§1.1. Установка нестандартного пакета DSPack
1.1.1. Компиляция и инсталляция
1.1.2. Установка маршрутов поиска.
§1.2. Создание шаблона программы с обращением к веб-камере
1.2.1. Модули, компоненты формы и глобальные переменные
1.2.2. Составление списка устройств.
1.2.4. Действия при создании формы
1.2.5. Замечание о начале обработки тика таймера
Часть 2. Проекты с веб-камерой
§2.1. Подготовительные упражнения
2.1.2. Отслеживание направление перемещения цветного пятна
§2.2. Задача исследования траектории пера ручки
2.2.3. Два режима выполнения подписи.
§2.3. Оцифровка траектории подписи и ее обработка
2.3.1. Действия в главном окне.
2.3.2. Действия в окне постобработки.
Область
применения веб-камер можно без преувеличения назвать обширной: скайп и
видеоконференции, охранная деятельность и организация выборов, контроль за транспортом и управление логистикой (например, сервис
DorogaTV). Не
умаляя важность вышеперечисленных направлений применения веб-камер, заметим все
же, что искусство программирования веб-камер оттачивается … в играх, точнее,
− в игровых приставках типа Xbox, Playstation. Так, например, система
Kinect, которая устанавливается на Xbox, позволяет распознавать и передавать в
игру движения человека.
Цель данного пособия – оказать
помощь в написании курсовых работ по программированию веб-камер. Текст разбит
на две части. В первой части излагаются вопросы установки компонентов Delphi 7.0, не входящих в стандартную
поставку, и излагаются шаблонные действия по применению этих компонентов в
программах Delphi 7.0 для работы с веб-камерой. Во второй части рассмотрены три проекта, интегрированных
с веб-камерой. Первый из них ограничивается выводом видеопотока в окно, второй
более функционален – распознает и возвращает в программу движение пятна
указанного цвета, и, наконец, третий может послужить каркасом полномасштабной
курсовой работы и решает задачу усиления качества идентификации рукописной
подписи в процессе ее выполнения.
1.1.1. Компиляция и инсталляция. Прежде всего, разумеется, необходимо
убедиться, что компьютер снабжен качественной веб-камерой.
По соотношению «цена/качество» некоторые специалисты рекомендуют, например, Genius VideoCam Look. В ряде случаев,
в частности, для решения сформулированной выше задачи, удобно использовать выносную веб-камеру (см. также в [1] интересный совет приспособить
для соответствующих целей телефон).
Для составления в Delphi программ, способных работать с веб-камерой, рекомендуется
скачать DSPack с сайта [2]. Возможны и другие подходы. DSPack −
это бесплатно распространяемая надстройка над DirectShow и DirectX, состоящая
из набора компонентов, позволяющих работать с потоками мультимедиа, в том числе
и с устройствами видеозахвата (веб-камерой, ТВ-тюнером и т.д.).
DSPack и DirectShow9 не
входят в состав стандартной поставки Delphi и позволяют отказаться от надежд на стандартный
для Delphi компонент TMediaPlayer. После ряда неудачных попыток мы можем вывести
осторожное заключение, что применить TMediaPlayer в online-режиме для извлечения из avi-файла
очередных кадров не удается.
Необходимо найти
DSPack\packages\DirectX9_D7.dpk, запустить и скомпилировать, а пакеты DSPack_D7.dpk и DSPackDesign_D7.dpk инсталлировать
(формальная разница прояснится ниже).
Отметим, что общем
случае пишут DirectX9_Dx.dpk и DSPack_Dx.dpk, где x
– номер используемой версии Delphi.
Приведем подробности установки
DSPack. Откроем файл DirectX9_D7.dpk двойным щелчком (запускается
программа
Рис. 1.
Подробности установки DSPack.
Delphi) и нажмем на кнопку compile (игнорируя возможное сообщение об
ошибке). В окне отобразят имена файлов из папки src; ниже нам будет нужно запомнить
папки установки модулей, чтобы прописать их в маршрутах поиска. Можно нажать кнопку
Options
(она видна на рис. 1
правее затененной кнопки install) в панели быстрых инструментов окна, затем во вкладке
Description
нового окна «Project Options for DirectX9_D7.bpl» выбрать радиокнопку «Designtime and Runtime». Последнее действие избыточно, т.к.
соответствующая кнопка выбрана по умолчанию.
Действия с файлами
DSPack_D7.dpk и DSPackDesign_D7.dpk из папки ../DSPack/packages отличаются от
описанных тем, что при двойном щелчке на них уже кнопка install доступна, после нажатия на нее
выводится сообщение об успешной инсталляции. Например, после установки
DSPackDesign_D7.dpk выводится сообщение, приведенное на рис. 2.
Рис. 2. Завершение инсталляции.
Действительно, в полном
соответствии с данным сообщением в среде Delphi, более точно, в библиотеке
визуальных компонентов (VCL) появится новая вкладка DSPack с компонентами (слева направо): FilterGraph (DSPack) VideoWindow (DSPack) SampleGrabber (DSPack) Filter (DSPack). На этой страничке находятся (см.
рис. 3) еще три компонента (ASFWirter, DSTrackBar, DSVideoWindowEx2 – все из модуля DSPack), но простые программы, подобные
рассмотренным во второй части, могут быть написаны без их использования.
1.1.2. Установка маршрутов поиска. Далее выбираем одно из двух альтернативных
действий. Первое: в папку с пустым проектом Delphi скопировать модули
DirectShow9.pas, DsPack.pas, DSutil.pas. В результате папка будет выглядеть примерно
так, как показано на рис. 4.
Рис. 3.
В VCL Delphi
появилась новая страничка DSPack с 7
компонентами: слева направо FilterGraph, VideoWindow, SampleGrabber, Filter, ASFWirter, DSTrackBar, DSVideoWindowEx2.
Это действие привлекает лаконичностью.
Но если у нас будет много аналогичных проектов, то в папке каждого из них будет
дублироваться один и тот же набор файлов.
Рис. 4.
Примерное содержимое папки. Уточнение см. ниже в «Краткой инструкции», п. 1.2.6.
Альтернативой этому
действию является установка в проекте Delphi путей поиска – src\directX9 и src\DSPack (или, как это сделано в примере
ниже, содержимое этих двух папок скопировать в одну папку C:\Install2013\ DirectX9_DSPack и указать ее в качестве пути поиска). Для этой цели откройте в Delphi 7.0 меню «Projects - Options...». В открывшемся диалоге перейдите по
дереву вкладок «Directories/Conditionals». В этой вкладке есть
раздел «Directories», в нем − поле редактирования Search Path и справа от него − кнопка для выбора (с
тремя точками). Нажмем на эту кнопку и в открывшемся окне «Directories» (со
списком маршрутов поиска, может быть, пустым) нажмем на кнопку с тремя точками
(см. справа-внизу) и в открывшемся диалоговом окне «Обзор папок» с древовидной
структурой папок выберем изображение нужной папки, файлы из которого необходимо
поместить в зону видимости Delphi. После выбора папки и подтверждения ее добавления
в список путей поиска (кнопка Add) остается подтвердить (кнопка
Ok), а затем нажать кнопку Ok также и в окне Options.
|
|
|
|
|
|
||
|
|
Рис. 5. Подробности установки путей
поиска.
Пример прописанного пути поиска:
C:\Install2013\DirectX9_DSPack.
1. Разместим на форме компоненты Image1, timer1 и еще четыре компонента: VideoWindow1,
SampleGrabber1, Filter1, FilterGraph1 с новой странички VCL (это последняя страница DSpack, см. рис. 2).
Свойство .Mode компонента FilterGraph1 нужно
установить в gmCapture, т.к. планируется работать с захватом видео, а значение
свойства .FilterGraph у первых трех компонентов установим в значение
FilterGraph1; для этой цели можно использовать инспектор объектов, но обзор текста
программы улучшится, если выполнить соответствующие действия в обработчике
события OnCreate или OnActivate. В результате в модуле нашего
проекта появятся объявления:
FilterGraph1: TFilterGraph;
VideoWindow1: TVideoWindow;
SampleGrabber1:
TSampleGrabber;
Filter1:
TFilter;
Timer1: TTimer;
Image1: TImage;
2. Добавить в предложение Uses модули Dspack, DSUtil, DirectShow9 в предположении, что пути поиска в
проекте установлены, как сказано выше.
3. В раздел глобальных переменных
добавить переменную типа TSysDevEnum.
var
Form1: TForm1;
VideoDevice: TSysDevEnum;
Приведем краткие разъяснения к объявленным переменным и размещенным на
форме компонентам. Переменная VideoDevice типа TSysDemEnum будет
участвовать в формировании списка всех устройств (фильтров) видеозахвата, присутствующих в системе.
Основное понятие DirectShow, а DSPack
базируется именно на нем, − это «граф фильтров», который включает набор
элементов (фильтров), соединенных в определенном порядке и характеризующих
источник аудио- и/или видеопотока, а также способ его
обработки, например, декодирование потоковых данных.
Компонент
VideoWindow1 предназначен
для отображения картинки с устройства видеозахвата. Если свойство FullScreen
этого компонента установить в True, то видео будет транслироваться на весь
экран. Компонент Filter1 нужен для управления фильтром, именно для него
установим веб-камеру в качестве источника видеосигнала. С помощью
SampleGrabber1 будет осуществляться захват видеопотока.
Если планируется использовать в
программе сразу несколько веб-камер, то в самом начале работы программы,
следует составить их список, а для последующего выбора нужного из них
использовать компоненты TListBox или TComboBox. Подробное описание
составления списка можно найти в [3].
В нашей программе можно обойтись без
списка, так как планируется использовать одну-единственную
веб-камеру, поэтому для краткости ограничимся следующим упрощенным вариантом. Вызовом
конструктора класса TSysDevEnum
с параметром
CLSID_VideoInputDeviceCategory создается описание устройств видео; если взять
CLSID_VideoCompressorCategory в качестве параметра, получим список
установленных в системе кодеков; при CLSID_CWaveInClassManager – описание устройств
(понятно, что и тип переменной в левой части следует выбрать соответственно).
Действия при создании
основного окна программы.
{1}
FilterGraph1.Mode:= gmCapture; {перевод в
режим выхватывания; отсутствие данного присвоения повлечет сообщение, что
интерфейс не поддерживается.}
VideoWindow1.FilterGraph:=FilterGraph1;
SampleGragger1.FilterGraph:=FilterGraph1;
FilterGraph:=FilterGraph1;
В результате построен граф фильтров, где компонент Filter1 воспринимает видеосигналы с веб-камеры, компонент SampleGrabber1 осуществляет захват последовательных фреймов, а компонент VideoWindow1 отображает видеопоток.
{2}
VideoDevice:=TSysDevEnum.Create
(CLSID_VideoInputDeviceCategory);
…
Здесь построен список видеоустройств,
количество последних «автоматически» заносится в свойство VideoDevice.CountFilters; на случай пустого списка предусмотрим
завершение программы:
if VideoDevice.CountFilters<=0 then
begin
ShowMessage ('Камера не найдена'); Close
end;
Следующим действием будет выбрано
устройство с номером 0, соответствующее продолжение приводится в п. 1.2.4.
Когда к компьютеру подключена лишь
одна веб-камера, достаточно проверить, что количество веб-камер VideoDevice. CountFilters отлично от нуля. Список устройств нумеруется с нуля, поэтому устройство в
начале списка имеет номер 0.
1.2.3.
Отступление (справка о DirectShow). Материал пункта приведен для
полноты изложения и для удобства восприятия остального материала, непосредственно
в нашем программном обеспечении не используется.
DirectShow – архитектура (API) для
воспроизведения, перехвата и обработки потоков мультимедиа. Любой видео поток представляет собой
последовательность кадров (фреймов).
Возможны разные способы обработки аудио и видео-потоков: раскодировка, копирование, изменение и т. п.
В DirectShow все эти
операции реализованы в виде COM-объектов,
в так называемых фильтрах. Фильтр служит единицей операции в DirectShow.
Каждый фильтр – это программный компонент, который встраивается в поток
мультимедийных данных и может выполнять определенные действия:
- читать
данные из файла;
- получать
видео непосредственно с источника;
-
декодировать форматы;
-
передавать данные на графическую или звуковую плату.
В
DirectShow имеются готовые фильтры, для которых можно определить разные цепочки
обработки. Итоговая конструкция представляет собой ориентированный граф (Filter
Graph). Для создания схемы соединения фильтров предназначен самый базовый и
лежащий в основе всех компонентов DirectShow компонент с названием Filter Graph
Manager (менеджер графа фильтров).
Рис. 6. Пример
фильтра графов: расщепление avi-файла на
звук и видео.
В этом примере пять фильтров, первый
(File Source) просто читает данные с диска, второй фильтр (AVI Splitter)
разделяет данные на кадры и передает упакованные видео данные фильтру AVI
Decompressor, который их распаковывает и передает фильтру Default DirectSound
Device, выводящему звук. AVI Decompressor передает распакованные данные фильтру
Video Renderer, который выводит кадры видео на экран.
Возвращаясь к созданию шаблона программы
для захвата видео с камеры и последовательного вывода его кадров на компонент
Timage, опишем три действия при создании формы, дополнительные к вышеизложенным
выше действиям 1 и 2.
{3} Укажем, что захват будет
производиться с камеры с номером 0:
FilterGraph1.ClearGraph;
FilterGraph1.Active := false;
Filter1.BaseFilter.Moniker :=
VideoDevice.GetMoniker(0);
FilterGraph1.Active
:= true;
Моникер в
COM-технологиях − специальный COM-объект, который отыскивает в сети,
создает, инициализирует экземпляр объекта и возвращает клиенту указатель на
него (переводится как «имя», «прозвище»).
Заметим, что при
работе со списком камер, размещенном, например, в компоненте ComboBox1, в
обработчике выбора из этого компонента естественно написать
VideoDevice.GetMoniker (ComboBox1.ItemIndex)
вместо приведенного здесь VideoDevice.GetMoniker(0).
{4} Вызовем метод визуализации потока RenderStream объекта FilterGraph1 с такими параметрами, чтобы уточнить:
откуда (Filter1), как (SampleGrabber1) и куда (VideoWindow1) направляются фреймы видео. Везде
применяется операция преобразования типов времени выполнения – as:
(FilterGraph1
as ICaptureGraphBuilder2). RenderStream
(@PIN_CATEGORY_PREVIEW,
nil,
Filter1 as
IBaseFilter,
SampleGrabber1
as IBaseFilter,
VideoWindow1 as IbaseFilter);
{5} Затем запустим вывод изображения:
FilterGraph1.Play;
Здесь же выполним
дополнительные действия – создадим переменную b1 типа TBitMap:
b1:=TBitmap.Create;
и выключим таймер впредь до
нажатия некоторой кнопки с надписью ‘Start’:
timer1.Enabled:=false.
С каждым тиком таймера из видеопотока
будет выхвачен очередной кадр и помещен в компонент Image1:
samplegrabber1.GetBitmap(b1);
Image1.Picture.Bitmap:=b1;
Подчеркнем, что выбор значения
свойства timer1.interval должен быть согласован с характером конкретной
программы. Ограничимся здесь этими обязательными действиями для вывода
очередных кадров в компонент Image1. Они завершают создание шаблона программы
для работы с веб-камерой.
Следующая краткая инструкция
призвана послужить конспектом, в которую рекомендуется подглядывать в первых
опытах программирования с веб-камерой.
Скомпилировать DirectX9_D7.dpk, DSPack_D7.dpk и инсталлировать DSPackDesign_D7.dpk. В результате в VCL Delphi появится
новая страничка DSPack с 7 компонентами: слева направо FilterGraph, VideoWindow, SampleGrabber, Filter, ASFWirter, DSTrackBar, DSVideoWindowEx2.
Создать проект Delphi 7 и
установить маршруты поиска на папку с модулями (см. справа-выше).
Разместить на форме четыре компонента: VideoWindow1, SampleGrabber1, Filter1, FilterGraph1 с новой странички DSpack. Свойство .Mode компонента FilterGraph1 нужно установить в gmCapture, т.к. планируется работать с
захватом видео, а значение свойства .FilterGraph у первых трех компонентов установить
в значение FilterGraph1.
Добавить в предложение Uses модули Dspack, DSUtil, DirectShow9.
В раздел глобальных переменных
добавить переменную типа TSysDevEnum: VideoDevice: TSysDevEnum;
Действия при создании основного окна программы (OnCreate).
1)
Filter1.FilterGraph:=FilterGraph1;
SampleGragger1.FilterGraph:=FilterGraph1;
VideoWindow1. FilterGraph:=FilterGraph1;
2) VideoDevice:=TSysDevEnum.Create (CLSID_VideoInputDeviceCategory);
Здесь построен список видеоустройств, их количество
заносится в свойство VideoDevice.CountFilters. Предусмотреть завершение программы, если список пуст:
if VideoDevice.CountFilters=0
then
begin
ShowMessage ('Камера не
найдена'); Close
end;
3) Указать, что захват будет производиться камерой с номером 0:
FilterGraph1.ClearGraph;
FilterGraph1.Active
:= false;
Filter1.BaseFilter.Moniker
:=
VideoDevice.GetMoniker(0);
FilterGraph1.Active
:= true;
4) Вызвать метод визуализации потока RenderStream объекта FilterGraph1 с параметрами, призванными уточнить: откуда (Filter1), как (SampleGrabber1) и куда (VideoWindow1) направляются фреймы видео:
(FilterGraph1 as ICaptureGraphBuilder2). RenderStream(@PIN_CATEGORY_PREVIEW,
nil, Filter1 as IBaseFilter, SampleGrabber1 as IBaseFilter,
VideoWindow1 as IbaseFilter);
5) Запуск: FilterGraph1.Play;
6) В обработчике тика таймера выполнить действия по помещению
очередных кадров в графический контейнер:
samplegrabber1.GetBitmap(b1);
Image1.Picture.Bitmap:=b1;
Упражнение 1. Вывести последовательные кадры
видеопотока в компонент TImage.
Рис. 7. Форма с
компонентами.
//Листинг программы к первому
упражнению.
unit Unit1;
interface
{1}
uses dspack, dsutil, DirectShow9, Windows, Messages, SysUtils,
Variants, Classes, Graphics,
Controls, Forms, Dialogs, ExtCtrls;
type
TForm1 = class(TForm)
FilterGraph1: TFilterGraph;
VideoWindow1: TVideoWindow;
SampleGrabber1:
TSampleGrabber;
Filter1: TFilter;
Image1: TImage;
Timer1: TTimer;
procedure
FormActivate(Sender: TObject);
procedure
Timer1Timer(Sender: TObject);
procedure
FormCreate(Sender: TObject);
end;
var
Form1: TForm1;
VideoDevice: TSysDevEnum;
b1: Tbitmap;
implementation
{$R *.dfm}
procedure TForm1.FormActivate(Sender: TObject);
begin
b1:=Tbitmap.Create;
{2}
VideoDevice:=TSysDevEnum.Create(CLSID_VideoInputDeviceCategory);
if VideoDevice.CountFilters
<=0 then
begin
ShowMessage ('Камера не найдена.'); close end;
{3}
FilterGraph1.ClearGraph;
FilterGraph1.Active:=false;
Filter1.BaseFilter.Moniker:=VideoDevice.GetMoniker(0);
FilterGraph1.Active:=true;
{4}
(FilterGraph1 as
ICaptureGraphBuilder2).RenderStream(
@PIN_Category_Preview, nil,
(filter1
as IBaseFilter),
(SampleGrabber1 as
IBaseFilter),
(VideoWindow1 as IBaseFilter)
);
{5}FilterGraph1.Play;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin {6}
SampleGrabber1.GetBitmap(b1);
Image1.Picture.Bitmap.Assign(b1)
end;
end.
Рис. 8. Вывод
видеопотока (слева) и отдельных кадров (справа). Заметно изменение
масштаба.
Упражнение 2. Программа должна организовать вывод последовательных кадров
видео в TImage. После выполнения щелчка кнопкой мыши на уникальном цветном пятне
(например, на красном колпачке ручки) программа распознает движение пятна влево
или вправо, отслеживая направление движения выводом соответствующей надписи на
метке и увеличением (при движении вправо) или уменьшением (при движении влево)
размеров окружности, выводимой на канву TImage.
Приведем полный текст программы.
unit Unit1;
interface
uses dsPack, dsUtil, DirectShow9, Controls, Windows, Messages, SysUtils,
Variants, Classes, Graphics, Forms,
Dialogs, ExtCtrls, StdCtrls, ComCtrls;
type
TForm1 = class(TForm)
FilterGraph1: TFilterGraph;
SampleGrabber1:
TSampleGrabber;
VideoWindow1: TVideoWindow;
Filter1: TFilter;
Image1: TImage;
Timer1: TTimer;
StatusBar1: TStatusBar;
LabeledEdit1: TLabeledEdit;
LabeledEdit2: TLabeledEdit;
LabeledEdit3: TLabeledEdit;
Label1: TLabel;
procedure
FormActivate(Sender: TObject);
procedure
Timer1Timer(Sender: TObject);
procedure
Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y:
Integer);
private
{ Private
declarations }
public
{ Public
declarations }
end;
//--------------------------------------------
var
Form1: TForm1;
videoDevice:
TSysDevEnum;
b:
Tbitmap;
t1: boolean=true;
delta:
integer = 20;
r0, g0, b0: byte;
xPred:
integer;
radius:
integer=100;
implementation
{$R *.dfm}
//--------------------------------------------
procedure TForm1.FormActivate(Sender: TObject);
begin
xPred:=Image1.Width;
b:=Tbitmap.Create;
VideoDevice:=TSysDevEnum.Create
(CLSID_VideoInputDeviceCategory);
if
VideoDevice.CountFilters<=0 then
begin
showMessage ('Нет камеры?'); close end;
FilterGraph1.active:=False;
Filter1.BaseFilter.Moniker:= VideoDevice.GetMoniker(0);
FilterGraph1.Active:=true;
(FilterGraph1
as ICaptureGraphBuilder2).
RenderStream
(@PIN_Category_Preview, nil,
(Filter1 as IBaseFilter),
(SampleGrabber1 as IBaseFilter),
(VideoWindow1 as IBaseFilter)
);
FilterGraph1.Play;
end;
//--------------------------------------------
procedure TForm1.Timer1Timer(Sender: TObject);
var
x, y,
x1, y1: integer;
c1: TColor;
r1, g1, b1: byte;
begin
x1:=Image1.Width
div 2;
y1:=Image1.Height div 2;
SampleGrabber1.GetBitmap(b);
Image1.Picture.Bitmap.Assign(b);
if not
t1 then //Этап слежения
With Image1.Canvas do
BEGIN
pen.color:=clRed;
Brush.Color:=clRed; Brush.Style:=bsSolid;
for
x:= 0 to Image1.Width-1 do
for
y:=0 to Image1.Height-1 do
begin
c1:=Image1.Canvas.Pixels[x,y];
r1:=GetRValue (c1);
g1:=GetGValue (c1); b1:=GetBValue (c1);
if
(abs (r0-r1)<delta) and (abs (g0-g1)<delta)
and
(abs (b0-b1)<delta) then
begin
ellipse
(x-3, y-3, x+3, y+3);
pen.Color:=clYellow;
brush.Style:=bsClear;
if
x<xPred then begin
label1.Caption:='<<<<<<<<<<<Налево<<<<<<<<<<'; dec (radius);
end;
if
x>xPred then begin
label1.Caption:='>>>>>>>>>>>Направо>>>>>>>>>'; inc (radius);
end;
Ellipse (x1-radius,
y1-radius, x1+radius, y1+radius);
xPred:=x;
exit;
end;
end;
END;
end;
//--------------------------------------------
procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y:
Integer);
var
c:
Tcolor;
begin
if t1
then
begin
c:=Image1.Canvas.Pixels[x,y];
r0:=GetRValue (c);
g0:=GetGValue (c); b0:=GetBValue
(c);
LabeledEdit1.Text:=IntToStr
(r0);
LabeledEdit2.Text:=IntToStr
(g0);
LabeledEdit3.Text:=IntToStr
(b0);
StatusBar1.SimpleText:=
Format ('Цвет выбран: r=%d, g=%d, b=%d', [r0, g0, b0]);
t1:=false;
Label1.caption:='Устойчивое слежение';
end;
end;
end.
|
|
Рис. 9. Программа распознает перемещение
красного колпачка и сопровождает соответствующей надписью и увеличением (уменьшением)
радиуса окружности перемещение вправо (влево). При цветной печати на конце
красного пятна заметна точка, наносимая программой в демонстрационных целях. |
Перейдем к более содержательной
задаче. Одним из классических способов идентификации, применяемых в юридической
практике, банковском деле и других областях, является, как известно, подпись
(более точно, рукописная подпись).
Имеются два независимых способа идентификации по подписи: идентификация по
рисунку подписи на документе и идентификация с учетом особенностей процесса ее
выполнения. Для идентификации рукописных подписей можно попытаться привлечь
методы распознавания рукописных букв, но понятно, что распознавание каждой
подписи превращается в отдельную задачу (подобную распознаванию каждой буквы,
но бо́льшей сложности). Предположим, что гипотетическая
программа для решения этой задачи apriori существует. Некий злоумышленник,
выдающий себя за клиента учреждения, способен подменить документ, удостоверяющий
клиента (или злоумышленник является внешне неотличимым «клоном» подлинного
клиента – возможно, близнец). При этом данный уголовный элемент обладает столь
развитыми способностями, что они позволяют в точности воспроизвести
«статический» вариант рукописной подписи клиента. Для усиления надежности идентификации
подписи можно предложить дополнительно проверить также: а) скорость выполнения
отдельных фрагментов подписи, б) проверку направления перемещения пера по линии
при выполнении того или иного участка подписи. Если учреждение уже имеет эталонную
(видео) запись подписи клиента или же извлеченную из нее информацию,
достаточную для выполнения проверки.
Предложенные далее алгоритм
и программное обеспечение в определенной мере справляются с этой задачей
усиления надежности идентификации.
2.2.1.
Идентифицирующее пятно. Пусть перед веб-камерой (желательно, выносной и
направленной вниз) расположен белый лист бумаги, на котором лежит ручка, где область пера выделена цветом, отличным
от фона. Например, ручка ‑ красного цвета, а поблизости от острия пера
нанесена заметная точка синего цвета – идентифицирующее пятно. Как будет
отслеживаться движение данного пятна, если предположить на минуту, что это
цвет, «строго» отличный от цветов всех видимых веб-камерой точек? Пусть даже при
дополнительном предположении, что это – чисто синий цвет? Понятно, что чистый
синий цвет (когда в представлении модели RGB красная и зеленая составляющие равны 0,
а синяя составляющая равна 255) едва ли можно реализовать на практике. С другой
стороны, если же значения реального цвета идентифицирующего пятна задать
приблизительно, то трудно оценить последствия такого подхода. Следовательно,
надо попытаться узнать, что сама программа «думает» о цвете данного пятна, т.е.
вычислить цвет программным путем.
В процессе решения упражнения 2 мы
научились указывать мышкой пятно на кадре, затем опросить его цвет программным
путем; дополнительно установим «практически приемлемую» близость цветов с
помощью компонента TSpinEdit.
Запланируем
следующую обработку нажатия на кнопку START:
delta:= spinEdit1.value;
SampleGrabber1.GetBitmap(b1);
Image1.Picture.Bitmap:=b1;
b2:=Image1.Picture.Bitmap;
{При нажатии на кнопку START текущий кадр видео занесли в переменную b1 и
скопировали в b2 и
Image1}
timer1.enabled:=true;
CurXY:=0; RealXY:=0;
{затем запустили таймер и обнулили
номер текущей выбранной точки CurXY пишущего пера и счетчик выбранных точек
RealXY}
Как видно из данного
кода, при нажатии на кнопку Start, кроме отображения
выхваченного (одиночного) кадра видеопотока в b1,
b2 и Image1 и инициализации переменных
delta (допустимая погрешность цветовых составляющих), curXY (номер точки пера) и RealXY (количества выбранных для
усреднения точек цветового пятна), происходит запуск таймера.
Поэтому необходимо
определить код, который будет целесообразно сопоставить каждому тику таймера.
Для технических целей разработчику
данной программы желательно иметь несколько визуальных компонентов и несколько
глобальных переменных, свойства которых, соответственно надписи (более точно,
первый цифровой символ надписи) и значения, принимают значения, однозначно
определяющие контекст выполнения. В качестве визуальных компонентов удобно
выбрать метки. Ограничимся четырьмя из них, которые назовем «Определитель»,
«Чернильница» ‑ показ в окне анимированной
ручки с чернильницей цвета наконечника пера или закрытие этого окна;
«Процесс» ‑ приглашение начать (нажатием
клавиши F1) выполнение подписи с заполнением RealXY значений массивов x и y точек траектории подписи или же завершить
процесс (нажатием клавиши F2);
«Воспроизведение» ‑ компонент становится
видимым лишь по завершении подписи, его выбор приводит к визуализации
выполненной подписи в соответствии с массивами x и y. Всюду ниже для удобочитаемости название
соответствующего компонента заменено на русское написание, например, Определитель.Caption и т.п.
Значение надписи компонента Определитель отслеживает следующие пять
контекстов обращения к обработке тика таймера:
(1) в начальный период,
(2) в период ручного указания на
цветовое пятно для вычисления его цвета,
(3) в период демонстрации устойчивого
отслеживания камерой движущего пятна с сопровождением в виде желтого кружочка,
(4) при выполнении подписи и,
наконец,
(5) после выполнения подписи.
При
этом начальный символ надписи в каждом из пяти перечисленных периодов совпадает
с номером периода.
Для
обработки тика таймера используются две переменные p1 и p2 типа pRGBArray, переменные r, g, b для цветовых составляющих
текущей точки, пробегающей все точки очередного кадра видео, и переменные i, j
для
записи координат текущего идентифицирующего пятна.
В
случае (4) наращивается счетчик точек подписи CurXY и в очередные ячейки глобальных
массивов
x и y записываются i и j:
x[CurXY]:=i; y[CurXY]:=j.
Приведем
код обработчика тика таймера.
begin
S:= Определитель.Caption;
{В
интерактивном режиме из компонента формы выберем меру близости цвета (каждой
составляющей цвета)}
delta:= spinEdit1.value;
{выберем
очередной кадр видео в растровую переменную b1}
if s[1] in ['2', '3', '4'] then
samplegrabber1.GetBitmap(b1);
{Выполним
перебор всех точек каждой строки растра b1, проверяя для цвета (r,g,b)
каждой точки строки, является ли он цветом уникального идентифицирующего пятна
(см. в предыдущем параграфе) с точностью delta}
for j:=3 to b1.Height-1 do
begin
p1 := pRGBArray(b1.scanline[j]);
for i:= 0 to b1.Width-1 do
{в
строке j выбрали точку с номером i}
begin
r:= p1[i].rgbtRed; g:= p1[i].rgbtGreen;
b:=p1[i].rgbtBlue;
{r, g, b
− цветовые составляющие точки (j,i)}
if (abs(r-r0)<delta) and
(abs(g-g0)<delta) and (abs(b-b0)<delta)then
begin
{если они близки к искомым цветам на величину delta,
то выполняются следующие действия (малосущественные подробности кода здесь
пропущены)}
{1)
наращивается счетчик точек подписи (учитывается после начала выполнения
подписи)}
if s[1] in ['3', '4'] then
BEGIN
inc (CurXY); x[CurXY]:=i;
y[CurXY]:=j;
if CurXY>= 1900 then begin
timer1.Enabled:=false;
ShowMessage ('Слишком длинная
подпись!');
Exit;
end;
END;
{Отслеживается
переполнение массива этих точек с выходом из процедуры (в случае угрозы
переполнения) и цикл поиска идентифицирующего пятна прерывается}
goto go;
end;
end;
end;
{Здесь действия, выполняемые, если пятно не обнаружено. Действия
могут ограничиваться (и обычно ограничиваются) стандартным выходом из процедуры}
EXIT;
{К
метке go программа переходит лишь если найдено
пятно. Для демонстрации устойчивости его определения программа сопровождает его
желтым кружочком. Это сопровождение отсутствует, если компонент
Определитель имеет надпись 2Невод,
т.е. когда демо-этап завершен.}
go: b1.Canvas.Brush.Color:=clYellow;
if Copy (Определитель.Caption,1,6)<>'2Невод'
then b1.canvas.Ellipse(i-6,
j-6, i+6, j+6);
{Содержимое
растра b1 отобразим в компоненте Image1}
image1.Picture.Bitmap:=b1;
end;
Отметим,
что наиболее долговременным участком выполнения программы, который может
отрицательно отозваться на качественном отслеживании текущих координат пера в
реальном времени, является поиск пятна (конца пишущего пера) в очередном кадре
видео, ведь при этом мы должны перебрать все точки кадра, что даже при размерах
500*500 кадра составяет ¼ миллиона, для каждой из которых программа
должна выполнить нетривиальную работу сравнивания ее цвета с тем, что
установлен как цвет пера (извлечение трех составляющих цвета и сравнение каждой
из них).
Если использовать поиск
пятна в очередном кадре поблизости от его местонахождения в предыдущем кадре, то
выигрывая в скорости при «стандартных» ситуациях, программа попадает в сильную
зависимость от скорости перемещения пера. Поэтому предпочтительнее сохранить тотальный,
по всему кадру, поиск пятна, но применить индексируемый
метод Scanline для ускорения
доступа к пикселям (см. [5]):
for j:=3 to b1.Height-1 do
begin
p1 := pRGBArray(b1.scanline[j]);
for i:= 0 to b1.Width-1 do
{в
строке j выбрали точку с номером i}
begin
r:= p1[i].rgbtRed; g:= p1[i].rgbtGreen;
b:=p1[i].rgbtBlue;
. . . . . . . . . . . . . . . . . .
Здесь показано, как найти адрес j-й строки растра: p1:= pRGBArray(b1.scanline[j]).
Далее, индексируя
переменную p1, получаем доступ к каждому i-му пикселю p1[i] строки и используем свойства rgbtRed, rgbtGreen, rgbtBlue
для вычисления RGB-составляющих цвета пикселя.
Отступление 1. Поскольку наше решение учитывает лишь перемещение пера
по бумаге, то нас не должен, казалось бы, интересовать собственно цвет чернил
нашей ручки: ведь решающим является цвет идентифицирующего пятна, нанесенного
поблизости от конца пера. Но если цвет пятна совпадает с цветом чернил, то при
нанесении чернил на бумагу при движении пера в дальнейшем поиск «запутается» между точкой пятна и одноцветной
с ним точкой, нанесенной на бумагу чернилами.
Здесь два простых
решения: 1) либо рекомендовать выполнять прозрачную подпись, т.е. подпись
невидимыми чернилами (или подпись, не касаясь пером бумаги), 2) либо цвет
идентифицирующего пятна должен быть отличным от цвета чернил; см. рис. 10.
|
|
|
Рис. 10. а) цвет идентифицирую-щего пятна синий, а
цвет чернил красный. |
|
б) цвет пятна и цвет чернил
черный. Выполняется «прозрачная» подпись. |
Отступление 2. При тестировании программы выявится следующая
ситуация: нельзя ожидать постоянного по времени значения цвета пера, более
точно, постоянных по времени результатов его вычисления программой. Кроме
качества видеоустройства, здесь присутствует и влияние невидимых воздушных
потоков и малозаметные перепады освещения. Поэтому при переборе точек
очередного кадра нельзя требовать точного совпадения цветов; необходимо
ограничиться разумной мерой близости. Как видно из кода, приведенного выше,
близость определяется некоторой мерой delta, выбираемой из компонента
spinEdit1. Эмпирически выбирая значение delta равным тому или иным значением, на стадии
проверки устойчивости распознавания пятна можно заметить, как цветной кружочек,
выводимый программой, прицельно попадает на конец пера (и тогда пользователь завершает
настройку), или (при недостаточно выверенном значении) совершает малые движения
около конца пера.
По определенному сигналу «клиенту»
предлагается начать выполнение подписи. В начале выполнения подписи клиентом
представитель проверяющей стороны нажимает клавишу F1, при завершении подписи – клавишу F2. Напомним, что эти нажатия
эквивалентны щелчкам левой кнопкой мыши на метке Процесс (см. п. 2.2.2), где в результате нажатия надпись «1Начнем
выполнение подписи» заменяется надписью «2Завершим выполнение подписи» и
наоборот. В обработчике события OnKeyDown для формы отслеживается код нажатой
клавиши и при его равенстве VK_F1 или VK_F2 выполняются действия 1 или 2
соответственно.
Действие 1. Если надпись компонента Процесс
начинается с цифры «1», то счетчики точек подписи CurXY (номер текущей точки) и RealXY (количество всех точек)
инициализируются нулевыми значениями, Определитель
получает значение «4Выполняется подпись», опрос которого при обращении к
событию от таймера позволяет определить, что обрабатывается тик таймера в
период выполнения подписи и, естественно, надпись Процесс изменяется на «2Завершим выполнение подписи».
Напомним, что основным результатом обработки тика таймера во время выполнения
подписи является запоминание последовательных точек траектории подписи в
массивах x и y.
Действие 2. Надписи
компонентов Определитель и Процесс меняются на «1Начнем сызнова
выполнение подписи» и «5Подпись завершена», таймер
запускается и переменной realXY (количество точек подписи) присваивается значение CurXY. В завершение действия
компонент Воспроизведение делается
видимым.
На рис. 11 слева видна
рукописная подпись, где оттенки цвета точки траектории определяются соответствующими
моментами времени выполнения, прямая внизу получена вытягиванием
последовательных точек в линию.
Рис. 11.
Оцифровка траектории завершена.
На этом же рисунке справа
отображаются координаты пера (видна лишь заключительная часть). Массив координат последовательных точек траектории
конца пера сохранен, его обработка сосредоточена в окне «Постобработка»
(рис. 12).
Рис. 12.
Переход на форму «Постобработка».
Программа предлагает
задавать опорные точки, указывая интервал
Рис. 13.
Опорные точки показаны красным цветом.
между ними (в
точках), и визуализировать их (на рис. 13 опорные точки указаны с интервалом 1
и выделены красным).
Рис. 14. Промежуточный
кадр анимированного вытягивания подписи в линию.
Замысел заключается
в следующем. Поскольку опорные точки получены через равные промежутки времени,
то расстояние между двумя соседними опорными точками позволяет судить о
локальной скорости выполнения подписи: если расстояние мало, то скорость незначительная,
если за это же время пройдено большее расстояние пути, то и скорость большая.
Рис. 15.
Визуализация локальных скоростей различных фрагментов подписи.
Сначала найдем
числовые значения этих расстояний (скоростей) и отобразим на графике (рис. 15).
Выберем аналогичные
данные о наборе точек подписи из эталонного файла, выполним масштабирование,
уравнивая:
1) по горизонтали –
количество точек (методом интерполирования), 2) по вертикали – среднее значение
высоты (скорость).
Остается выполнить
стандартную математическую обработку дял оценивания близости двух равномощных
наборов точек и экспериментальным путем установить приемлемую меру близости.
Некоторые из фрагментов данной работы
были рассмотрены в [6]-[9].
Опыт, накопленный в процессе решения задачи идентификации подписи, может
пригодиться и при выполнении ряда курсовых работ, посвященных мультимедиа. Аналогичные
вопросы обсуждаются на различных сайтах (например, [10]).
В частности, как по фотографии человека (даже в режиме реального времени) или по
видео с веб-камеры выполнить идентификацию личности. Одним из шагов в этом
направлении является программа VeriLook algorithm demo [11],
которая сохраняет фото человека в базе и сравнивает картинку с эталонными
изображениями, полученными на предыдущем шаге.
Перспективным
представляется разработка программного обеспечения для работы с
видеоустройствами, где предусмотрено распараллеливание вычислений и создание
потоков (нитей) с предоставлением их для выполнения отдельным ядрам процессора.
1. SmartCam — превращаем смартфон в веб-камеру [Электронный ресурс]
/ URL: http://www.goldsoftware.ru/smartcam (дата обращения: 01.09.2017).
2.
Delphi Enterprise Edition 7.0 2001 + DSPack, DSP, JVCL Components [Электронный
ресурс] / URL: https://nnm-club.name/forum/viewtopic.
php?t = 110158 (дата обращения: 01.09.2017).
3. Шкрыль А. Программерская сигнализация //
Хакер, № 103,
стр. 112 (http://www.xakep.ru/magazine/xa/103/112/1.asp).
4. Есенин С. DirectX и Delphi. Разработка
графических и мультимедийных приложений. – СПб.: БХВ-Петербург,
2006. – 512 с.
5. Тюкачев Н. А., Свиридов Ю. Delphi. Создание мультимедийных приложений. Учебный курс. – СПб.: Питер, 2001. – 400 с.
6. Магомедов М. А. Динамические составляющие
проблемы повышения надежности идентификации // Труды молодых ученых. 2009г,
Естественные науки, изд-во ИПЦ ДГУ. С. 113-115.
7. Магомедов М. А.
К вопросу о динамическом распознавании // "Информационные технологии в
профессиональной деятельности и научной работе": сб. материалов
Всероссийской научно-практической конференции с междунар. участием. –
Йошкар-Ола, Марийский гос. тех.ун-т, ч.2. с. 116-118,
24-25 апреля 2009 г.
8. Магомедов
Т. А. Вычисление скорости выполнения фрагментов рукописной подписи //
Свидетельство № 2012616498 о гос. регистрации программы для ЭВМ 18 июля 2012 г.
в Реестре программ для ЭВМ.
9. Магомедов М. А.
Задача динамической идентификации // Актуальные проблемы математики и смежные
вопросы / Материалы междун. конф. «Мухтаровские чтения». – Махачкала, 2012 г.
(с. 109).
10. Specialized and general technologies for computer
vision, robotics and A.I. [электронный ресурс] / URL: http://www.neurotechnologija.com (дата
обращения: 01.09.2017).
11. Neurotec_Biometric_10_0_Algorithm_Demo_Win32_x86 [электронный ресурс] / URL: http: //www.neurotechnologija.com/download/vlook.zip (дата обращения:
01.09.2017).