Естественно-интуитивное взаимодействие с компьютером с использованием Intel RealSense SDK

Интеграция возможностей модулей Intel Perceptual Computing SDK для реализации многоуровневой системы информационной безопасности, основанной на биометрической информации

Показывать лекцию целиком

Дополнительные материалы к работе можно скачать здесь.

Описание прототипа приложения. Возможности SDK для применения биометрических методов

Поставим перед собой задачу - создание простого приложения с контролем доступа. То есть пользователю необходимо пройти несколько проверок чтобы доказать, что перед компьютером сидит именно он. Приложение будет позволять создавать нового пользователя и авторизоваться. Процесс авторизации похож на авторизацию на сайте, где пользователю необходимо ввести логин и пароль. Но кроме пароля приложение проверяет изображение с камеры, а также проводит проверку на "речевой" пароль - кодовое слово или короткую фразу, которые необходимо будет произнести. Только в случае совпадения всех характеристик (лицо пользователя идентифицировано, кодовое слово и пароль совпадают с исходными) авторизация завершается успехом. Создание нового пользователя представляет собой процесс создания исходных экземпляров, с которыми происходит сравнение в последующей авторизации: изображение с камеры для идентификации человека по лицу, произнесенного в микрофон кодового слова, пароль. Все данные будут храниться в локальной базе данных.

Определим модули Intel Perceptual Computing SDK, которые участвуют в работе приложения. Для создания и идентификации человека по лицу используется модуль Face Recognition интерфейса FaceAnalysis. Для распознавания кодового слова, которое пользователь продиктовал в микрофон, используется модуль Voice Recognition.

Создание приложения: Начало

Для экономии времени возьмем существующий пример приложения Face Tracking из папки /PCSDK/framework/CSharp/face_tracking.cs. Скопируем папку с проектом и запустим. Программа представляет собой приложение по отслеживанию лица человека. Так как в нашей программе требуется не отслеживание, а именно распознание лица, то удалим лишние части, оставив пункты меню Device - для выбора камеры, с помощью которой будет транслироваться видеопоток, Module - модуль Face Analysis, который понадобиться для реализации идентификации человека по лицу. Также оставим CheckBox Location, кнопки Start/Stop и StatusStrip Status2. Теперь, чтобы проект смог скомпилировался без ошибок, необходимо подкорректировать код: в MainForm.cs - удалим или закомментируем функции DrawLandmark, Live_Click, Playback_Click, GetPlaybackState, Recod_Click, GetRecordState, GetPlaybackFile, GetRecordFile, PopulateProfileMenu()

а также изменим функции Panel_Paint, UpdatePanel, Module_Item_Click, DoTracking следующим образом:

private void Panel_Paint(object sender, PaintEventArgs e)
        {
            lock (this)
            {
                if (bitmap == null) return;
                    e.Graphics.DrawImageUnscaled(bitmap, 0, 0);
                
            }
        }
private delegate void UpdatePanelDelegate();
public void UpdatePanel()
        {
            Panel2.Invoke(new UpdatePanelDelegate(delegate() 
                {
                    
                    Panel2.Invalidate();
                }));
        }
private void Module_Item_Click(object sender, EventArgs e)
        {
            RadioCheck(sender, "Module");            
        }

private void DoTracking()
        {
            FaceTracking ft = new FaceTracking(this);           
            
            ft.AdvancedPipeline();
           
            this.Invoke(new DoTrackingCompleted(
                delegate
                {
                    Start.Enabled = true;
                    Stop.Enabled = false;
                    MainMenu.Enabled = true;
                    if (closing) Close();
                }
            ));
        }
    

в FaceTracking.cs: удалим или закомментируем функцию SimplePipeline, класс MyUtilMPipeline и изменим функцию AdvancedPipeline, удалив оттуда следующее условие:

if (form.GetRecordState())
            {
                capture = new UtilMCaptureFile(session, form.GetRecordFile(), true);
                capture.SetFilter(form.GetCheckedDevice());
            }
            else if (form.GetPlaybackState())
            {
                capture = new UtilMCaptureFile(session, form.GetPlaybackFile(), false);
            }
            else
            {
                capture = new UtilMCapture(session);
                capture.SetFilter(form.GetCheckedDevice());
            }
    

Вместо него вставим строки:

capture = new UtilMCapture(session);
capture.SetFilter(form.GetCheckedDevice());
    

После этого программа должна успешно скомпилироваться и приложение будет выглядеть следующим образом:

Рис. 4.1. Приложение Face Tracking до и после преобразований

После этого добавим в проект форму, которая будет выполнять роль главного меню. Для этого через вкладку "Проект" необходимо выбрать "Добавить форму Windows", назовем ее mainmenu.cs. Добавим на нее два объекта label1 и label2 в свойствах Text, которых пропишем "текущий пользователь" и "Гость" соответсвенно. Также добавим две кнопки: button1, у которого свойство Text равно "Создать пользователя" и button2, со свойством Text равным "Авторизоваться".

Замечание: не путайте свойство Text и Name, первое отвечает за текст, связанный с элементом управления, а второе отвечает за имя, используемое для идентификации объекта в коде.

(рис 4.2) Примерный вариант формы mainmenu

Для того чтобы при запуске программы у нас появлялась форма mainmenu, а не MainForm необходимо открыть файл Program.cs (можно найти через "Обозреватель решений" в вкладке "Вид") и заменить часть кода в функции Main:

До: После:
PXCMSession session;
pxcmStatus sts= PXCMSession.CreateInstance(out session);
if (sts >= pxcmStatus.PXCM_STATUS_NO_ERROR)
{
  Application.Run(new MainForm(session));
  session.Dispose();
 }
        
Application.Run(new mainmenu());
        

Для начала опишем процесс создания нового пользователя: добавим в mainmenu.cs 3 глобальные переменные: username - уникальное имя пользователя, password - пароль, secretword - кодовое слово.

private string secretword = null;
private string username = null;
private string password = null;
    

Обработаем событие нажатия на кнопку button1 так, чтобы при нажатии форма увеличивалась, и появлялись дополнительные элементы, необходимые для создания нового пользователя. Предварительно добавим на форму label3, label4, с текстами "Имя" и "Пароль", textBox1,textBox2 и 3 кнопки button3, button4, button5 с текстами "Добавить изображение", "Добавить кодовое слово", "Создать" соответственно.

private void button1_Click(object sender, EventArgs e)
        {          
            textBox1.Text = "";
            textBox2.Text = "";
            username = null;
            password = null;
            secretword = null;
            this.Width = 580;
        }
    
(рис 4.3) Обработчик события нажатия на кнопку button1

Создание приложение: Подключение базы данных

Для создания и авторизации пользователей нам потребуется хранить данные о пользователей в базе данных. Для этого подойдет любая база данных, в этой работе используется база данных SQLCE (SQL Compact Edition). SQLCE может хранить данные используя язык С# и .NET фреймворк версии 3.5 и выше. Для того чтобы создать базу данных найдите в вкладке "Data" ("Данные") пункт "Add New Data Source" ("Добавить новый источник данных"). Затем выберите тип "Database" ("База данных") и нажмите "Далее", в типе модели выберите "Набор данных".

(рис 4.4)

Нажмите "Создать подключение", выберите источник данных Microsoft SQL Server Compact 3.5 (Поставщик данных среды .NET для Microsoft SQL Server Compact 3.5) используя "Мой компьютер". В свойствах подключения нажмите "Создать". Напишите имя файла в конце пути, например dataname.sdf, затем "OK". Используя кнопку "Проверить подключение" в левом нижнем углу: если проверка успешно закончилась, то нажимайте "Далее" и "Да", когда мастер-настройки спросит, копировать ли базу данных в проект. Когда база данных добавлена ее можно увидеть в "Обозревателе серверов" (пункт меню "Вид" или сочетание клавиш Ctrl+Alt+S). Нажмите на созданную базу данных и щелчком правой кнопки на пункте "Таблицы" выберите "Создать таблицу". Укажите имя таблицы "users" и добавьте следующие столбцы:

(рис 4.5) Столбцы таблицы users

Создайте таблицу models, в которой будем хранить изображения пользователей со следующими столбцами:

(рис 4.6) Столбцы таблицы models

Попробуйте вручную вставить несколько записей для того чтобы убедиться, что все работает верно.

Реализация метода распознавания лица. Создание модели лица. Идентификация

При создании нового пользователя необходимо сделать несколько исходных моделей пользователя, с которыми будет происходить последующее сравнение при процессе авторизации. Для этого потребуется вызвать форму с модулем распознавания лица из нашего меню при нажатии кнопки button3 ("Добавить изображение").

private void button3_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(textBox1.Text)) //имя пользователя не задано
            {
                MessageBox.Show("Введите имя пользователя");
                return;
            }
 
            username = textBox1.Text;
            PXCMSession session;
            pxcmStatus sts = PXCMSession.CreateInstance(out session); //создаем сессию
            if (sts >= pxcmStatus.PXCM_STATUS_NO_ERROR) 
            {
                MainForm ft = new MainForm(session, username); //создаем форму
                ft.Show();        // вызываем форму
                session.Dispose();//удаляем сессию
            }
        }
    

Также необходимо добавить глобальные переменные для имени пользователя, режима запуска формы( создание нового пользователя или идентификации) и изменить конструктор формы MainForm в MainForm.cs:

private string username; 
private int mode; // 1 - режим создание пользователя , 2 - режим идентификации
public MainForm(PXCMSession session,string  username)        {
            InitializeComponent();
            this.session = session;
            this.username = username;
            this.mode = 1; // режим создания пользователя
            PopulateDeviceMenu();  //получить список камер
            PopulateModuleMenu();           
            button1.Show();            

            FormClosing += new FormClosingEventHandler(MainForm_FormClosing);
            Panel2.Paint += new PaintEventHandler(Panel_Paint);
        }
    

Добавим на форму MainForm еще одну кнопку button1 (назовем ее "сделать снимок"), при нажатии на которую будет браться текущее изображение с камеры и создаваться модель лица человека.

Реализуем метод создания модели лица человека на основе метода AdvancedPipeline в FaceTracking.cs создадим функцию CreatePerson(), тело функции скопируем из метода AdvancedPipeline и изменим некоторые части кода.

До:

/* Display Results */
DisplayPicture(capture.QueryImage(images,PXCMImage.ImageType.IMAGE_TYPE_COLOR)); DisplayLocation(face);
form.UpdatePanel();
    

После:

/* Display Results */
if (form.create_model_triger == true) 
{
 DisplayPicture(capture.QueryImage(images, PXCMImage.ImageType.IMAGE_TYPE_COLOR));
 DisplayLocation(face, 2);//вывести лицо и сделать модель лица человека из текущего кадра
 form.create_model_triger = false;
}
else
{
 DisplayPicture(capture.QueryImage(images, PXCMImage.ImageType.IMAGE_TYPE_COLOR));
 DisplayLocation(face, 1); //вывести лицо
}
form.UpdatePanel();
    

public volatile bool create_model_triger = false; - глобальная переменная в MainForm.cs, которая необходимо чтобы сделать модель лица человека в нужный момент.

Изменим функцию DispayLocation(). Во-первых, добавим в нее атрибут int mode, а тело изменим следующим образом.

До:

private void DisplayLocation(PXCMFaceAnalysis ft) {
for (uint i=0;;i++) {
   int fid; ulong ts;
   if (ft.QueryFace(i, out fid, out ts) < pxcmStatus.PXCM_STATUS_NO_ERROR) break;

   /* Retrieve face location data */
   PXCMFaceAnalysis.Detection ftd = ft.DynamicCast<PXCMFaceAnalysis.Detection>(PXCMFaceAnalysis.Detection.CUID);
   PXCMFaceAnalysis.Detection.Data ddata;
   if (ftd.QueryData(fid, out ddata) >= pxcmStatus.PXCM_STATUS_NO_ERROR)
      form.DrawLocation(ddata);              
   }
}
    

После:

private void DisplayLocation(PXCMFaceAnalysis ft, int mode)// mode = 1 вывод, 2 - вывод и //создание
        {
            for (uint i=0;;i++) {
            int fid; ulong ts;
            if (ft.QueryFace(i, out fid, out ts) < pxcmStatus.PXCM_STATUS_NO_ERROR) break;

            /* Retrieve face location data */
            PXCMFaceAnalysis.Detection ftd = ft.DynamicCast<PXCMFaceAnalysis.Detection>(PXCMFaceAnalysis.Detection.CUID);
            PXCMFaceAnalysis.Detection.Data ddata;

            if (mode == 2)
            {                
                /* Создание и настройка модуля распознавания лиц */
                PXCMFaceAnalysis.Recognition ftr = ft.DynamicCast<PXCMFaceAnalysis.Recognition>(PXCMFaceAnalysis.Recognition.CUID);
                PXCMFaceAnalysis.Recognition.ProfileInfo rec_pinfo;
                ftr.QueryProfile(0,out rec_pinfo);
                PXCMFaceAnalysis.Recognition.Model modelCurr; // иницилизация модели
                pxcmStatus sts = ftr.CreateModel(fid, out modelCurr); //создание модели

                if (sts < pxcmStatus.PXCM_STATUS_NO_ERROR)
                    break;     
 
                byte[] temp = new byte[rec_pinfo.modelSize];
                /* Нахождение строки источника данных БД для подключения, указываем ту, что появилась при создании БД.
Альтернативно можно прописать абсолютный путь к БД, например:
conString = @"Data Source=C:\Users\1\Desktop\intel perceptual computing sdk\practice 2\MyDB1.sdf"; */
                string conString = practice_2.cs.Properties.Settings.Default.MyDatabase_1ConnectionString;

                using (SqlCeConnection con = new SqlCeConnection(conString))
                {
                    con.Open(); //открываем подключение
                    sts = modelCurr.Serialize(temp); // преобразуем модель в массив //байтов
                    //string name = "Пользователь 1";
                    string name = form.getUsername(); // получаем имя пользователя
                    var rowId = new SqlCeCommand("SELECT TOP(1) id FROM models ORDER BY id DESC", con).ExecuteScalar();// выберем максимальный Id в таблице
                    using (SqlCeCommand com = new SqlCeCommand("INSERT INTO models VALUES(@id,@name,@image)", con))
                    {
                        int newid = Convert.ToInt32(rowId) + 1; 
                        //int newid = 1;
                        com.Parameters.AddWithValue("@id", newid);
                        com.Parameters.AddWithValue("@name", name);
                        com.Parameters.AddWithValue("@image", temp);
                        com.ExecuteNonQuery(); // выполняем запрос
                    } 
                }
            }
            if (ftd.QueryData(fid, out ddata) >= pxcmStatus.PXCM_STATUS_NO_ERROR)
                    form.DrawLocation(ddata); // отрисовываем лицо          
        }
}
    

Осталось добавить в MainForm.cs функцию получения имени пользователя getUsername и изменить в функции DoTracking строчку ft.AdvancedPipeline() на ft.CreatePerson() и обработать событие о нажатии на кнопку button1.

public string getUsername()
        {
            return this.username;
        }
private void button1_Click(object sender, EventArgs e)
        {
            create_model_triger = true;
        }
    

Теперь можно попробовать создать модель, убедиться, что она появилась можно, выбрав пункт меню "Показать таблицу данных" у таблицы в Обозревателе серверов. Чем больше моделей сделает пользователь, тем больше вероятность, что модуль распознавания его идентифицирует.

(рис 4.7)

Создадим новую форму Form1.cs, которая будет открываться при нажатии на кнопку "Авторизоваться" формы mainmenu. Добавим на нее 2 textBox и 3 кнопки:

(рис 4.8) Форма авторизации Form1

Обработаем нажатие кнопки "Авторизоваться" button2 формы mainmenu:

private void button2_Click(object sender, EventArgs e)
        {
            Form1 f1 = new Form1(this);
            f1.Show();

        }
    

Тогда конструктор формы Form1 будет выглядеть следующим образом:

public Form1(mainmenu mainmenu1)
        {
            InitializeComponent();
            this.mm1 = mainmenu1;
        }
    

Объявим все глобальные переменные, которые понадобятся:

mainmenu mm1;
private string username = "Guest";
private string nameFromFaceRegognition;
private string wordFromVoiceRegognition;
private string password;
    

Обработаем событие нажатия на кнопку "Идентификация лица" button1 формы Form1, так чтобы при нажатии запускалась форма MainForm в режиме идентификации:

private void button1_Click(object sender, EventArgs e)
        {           
           PXCMSession session;
           pxcmStatus sts = PXCMSession.CreateInstance(out session);
           if (sts >= pxcmStatus.PXCM_STATUS_NO_ERROR)
           {
               MainForm ft = new MainForm(session,this); //создаем форму
               ft.Show();//вызываем форму
               session.Dispose();
           }
           
        }
    

Для этого потребуется определить дополнительный конструктор формы в MainForm.cs:

public MainForm(PXCMSession session, Form1 f1)
        {
            InitializeComponent();
            this.f1 = f1;  
            this.session = session;
            this.mode = 2; // режим идентификации
            PopulateDeviceMenu();
            PopulateModuleMenu();
            button1.Hide(); 
            FormClosing += new FormClosingEventHandler(MainForm_FormClosing);
            Panel2.Paint += new PaintEventHandler(Panel_Paint);

        }
    

button1.Hide() необходим, чтобы пользователь не мог создавать модели во время авторизации. Снова изменим фрагмент функцию DoTracking.

До: После:
ft.CreatePerson();
        
if (mode == 1)
            {
                ft.CreatePerson();
            }
else
            {
                ft.StartRecognition();
            }
        

Функция StartRecognition похожа на CreatePerson. CreatePerson выводит изображение с камеры, отслеживая лица в кадре, но в момент нажатия кнопки делает модель лица в текущем кадре и сохраняет его в базу данных. StartRecognition различается тем, что в начале забирает данные из базы данных, собирает список всех моделей, а затем выводит изображение с камеры, отслеживая лица в кадре, но в тоже время сравнивает их со списком всех моделей. При уровне совпадения больше заданного функция сообщает, что пользователь идентифицирован.

public void StartRecognition() {
...//объявляются переменные, точно также как в CreatePerson
PXCMFaceAnalysis.Recognition facer = face.DynamicCast<PXCMFaceAnalysis.Recognition>(PXCMFaceAnalysis.Recognition.CUID);
            PXCMFaceAnalysis.Recognition.ProfileInfo rec_pinfo;
            facer.QueryProfile(0, out rec_pinfo);
            facer.SetProfile(ref rec_pinfo);
form.UpdateStatus("Streaming");
            PXCMImage[] images = new PXCMImage[PXCMCapture.VideoStream.STREAM_LIMIT];
            PXCMScheduler.SyncPoint[] sps = new PXCMScheduler.SyncPoint[2];
            
            getListModel(face);  //получаем список моделей из БД      
            while (!form.stop)
            {
                      ...// точно также как в CreatePerson
            /* Display Results */
DisplayPicture(capture.QueryImage(images, PXCMImage.ImageType.IMAGE_TYPE_COLOR)); //вывод //изображения

                for (uint i = 0; ; i++) //для каждого найденного лица
                {
                    int fid; ulong ts;
                    if (face.QueryFace(i, out fid, out ts) < pxcmStatus.PXCM_STATUS_NO_ERROR) break; //если больше лиц не найдено, то выйти

                    /* Retrieve face location data */
                    PXCMFaceAnalysis.Detection ftd = face.DynamicCast<PXCMFaceAnalysis.Detection>(PXCMFaceAnalysis.Detection.CUID);
                    PXCMFaceAnalysis.Detection.Data ddata;
                   
                    PXCMFaceAnalysis.Recognition.Model modelCurr;
                    sts = facer.CreateModel(fid, out modelCurr); // создаем модель для //найденного лица
                    if (sts < pxcmStatus.PXCM_STATUS_NO_ERROR)
                        break;

                    int modelnumber = models.Count; //количество моделей
                    uint index;                  
                    Single[] scores = new Single[modelnumber]; // массив сходства моделей
                    sts = modelCurr.Compare(models.ToArray(), scores, out index); //сравниваем текущую модель со всеми моделями из списка, index - номер самой похожей
                    if (sts == pxcmStatus.PXCM_STATUS_NO_ERROR || sts == pxcmStatus.PXCM_STATUS_ITEM_UNAVAILABLE) 
                    {
                        if (scores[(int)index] > 0.99) //если сходство больше чем 99%
                        {                           
                            form.UpdateStatus("Пользователь "+namemodels[(int)index]+" идентифицирован"); // выводим сообщение о идентификации пользователя
                            if (ftd.QueryData(fid, out ddata) >= pxcmStatus.PXCM_STATUS_NO_ERROR)
                                form.DrawLocation(ddata); //выводим лицо
                        }
                    }
                }
                form.UpdatePanel();
            }
    

Подробнее остановимся на функции Compare. Ее можно использовать в нескольких видах:

pxcmStatus Compare(Model[] models, Single[] scores, out index);
pxcmStatus Compare(Model[] models, out index);
    

где models - массив моделей, scores - массив сходства моделей, он заполнится автоматически при выполнении функции, где scores[i] будет указывать на сходство текущей и i-ой модели, index - индекс наиболее похожей модели.

Осталось получить список всех моделей, то есть определить функцию getListModel:

List<PXCMFaceAnalysis.Recognition.Model> models = new List<PXCMFaceAnalysis.Recognition.Model>(); //коллекция моделей
       List<string> namemodels = new List<string>(); //коллекция имен пользователей, на // которые указывают модели
       
       public void getListModel(PXCMFaceAnalysis ft) //получение списка моделей
       {

           PXCMFaceAnalysis.Recognition facer = ft.DynamicCast<PXCMFaceAnalysis.Recognition>(PXCMFaceAnalysis.Recognition.CUID);
           PXCMFaceAnalysis.Recognition.ProfileInfo rec_pinfo;            
           facer.QueryProfile(out rec_pinfo);
           //facer.SetProfile(ref rec_pinfo);

           PXCMFaceAnalysis.Recognition.Model m;
           byte[] temp = new byte[rec_pinfo.modelSize];

            
               /* Нахождение строки источника данных БД для подключения, указываем ту, что появилась при создании БД.
Альтернативно можно прописать абсолютный путь к БД, например:
conString = @"Data Source=C:\Users\1\Desktop\intel perceptual computing sdk\practice 2\MyDB1.sdf"; */

        string conString = practice_2.cs.Properties.Settings.Default.MyDatabase_1ConnectionString;
        using (SqlCeConnection con = new SqlCeConnection(conString))
        {
            con.Open();//открываем подключение                
            using (SqlCeCommand com = new SqlCeCommand("SELECT name,buffer FROM models ORDER BY id DESC", con)) // 
            выбираем массив байтов модели и имя пользователя
            {
                SqlCeDataReader reader = com.ExecuteReader();
                while (reader.Read())
                {
                    string face_name = reader.GetString(0); //получить первый столбец //текущей строкий
                    reader.GetBytes(1, 0, temp, 0, (int)rec_pinfo.modelSize); //получить второй столбец текущей строки = в temp 
                    получаем считываемый массив байтов //модели
                    facer.DeserializeModel(temp, out m); //собираем модель из массива //байтов 
                    models.Add(m);
                    namemodels.Add(face_name);
                }
            }
        }

    }
    

Теперь после распознавания человека в статусной строке будет появляться надпись пользователь "имя пользователя" идентифицирован, изменим функцию обработчик кнопки Stop для того чтобы зафиксировать результат.

private void Stop_Click(object sender, EventArgs e)
        {
            if (mode == 2) //если режим идентификации
            {
                if (StatusLabel.Text.StartsWith("Пользователь") 
                 StatusLabel.Text.EndsWith("идентифицирован")) // пользователь идентифицирован
                {
                    int namelength = StatusLabel.Text.Length - 29;
                    username = StatusLabel.Text.Substring(13,namelength);
                    f1.UpdateDataFromFaceRecognition(username); //вернуть имя на форму f1
                }
            }
            stop = true;
            closing = true;
        }
    

Функция UpdateDataFromFaceRecognition просто запоминает имя пользователя:

public void UpdateDataFromFaceRecognition(string newUserName)
        {
            nameFromFaceRegognition = newUserName;
        }
    

Реализация метода распознавания речи

Для того чтобы создать метод распознавания речи нам потребуется новая форма Form2.cs. Добавьте на нее menuStrip1, три кнопки button1, button2, button3, а также treeView1 и treeView2. В menuStrip1 сделайте 1 пункт "Источник", который будет аналогом пункта меню "Devices" из MainForm.

(рис 4.9)

По аналогии обработайте событие нажатия кнопки button4 формы mainmenu и button2 формы Form1 так, чтобы вызывалась форма Form2.

private void button2_Click(object sender, EventArgs e)
    {             
        PXCMSession session;
        pxcmStatus sts = PXCMSession.CreateInstance(out session);
        if (sts >= pxcmStatus.PXCM_STATUS_NO_ERROR)
        {
            Form2 f2 = new Form2(session,this);
            f2.Show();
            session.Dispose();
        }
    }
        
private void button4_Click(object sender, EventArgs e)
    {          
        PXCMSession session;
        pxcmStatus sts = PXCMSession.CreateInstance(out session);
        if (sts >= pxcmStatus.PXCM_STATUS_NO_ERROR)
        {
            Form2 f2 = new Form2(session,this);
            f2.Show();                
            session.Dispose();
        }
    }
        

Соответсвенно, форма Form2 должна иметь два конструктора:

        Form1 f1;
        mainmenu mm1;
        public Form2(PXCMSession session, Form1 form1) //recognition
        {
            InitializeComponent();
            this.session = session;
            this.f1 = form1;
            PopulateSource();
        }

        public Form2(PXCMSession session, mainmenu mainmenu1) // creating
        {
            InitializeComponent();
            this.session = session;
            this.mm1 = mainmenu1;
            PopulateSource();
        }
    

Функция PopulateSource() исходна функции PopulateDeviceMenu() MainForm, которая отображает список приборов, которые можно использовать.

private void PopulateSource()
        {
            PXCMSession.ImplDesc desc = new PXCMSession.ImplDesc();
            desc.group = PXCMSession.ImplGroup.IMPL_GROUP_SENSOR;
            desc.subgroup = PXCMSession.ImplSubgroup.IMPL_SUBGROUP_AUDIO_CAPTURE;
            ToolStripMenuItem sm = new ToolStripMenuItem("Источник");
            for (uint i = 0; ; i++)
            {
                PXCMSession.ImplDesc desc1;
                if (session.QueryImpl(ref desc, i, out desc1) < pxcmStatus.PXCM_STATUS_NO_ERROR) break;
                PXCMCapture capture;
                if (session.CreateImpl<PXCMCapture>(ref desc1, PXCMCapture.CUID, out capture) < pxcmStatus.PXCM_STATUS_NO_ERROR) 
                continue;
                for (uint j = 0; ; j++)
                {
                    PXCMCapture.DeviceInfo dinfo;
                    if (capture.QueryDevice(j, out dinfo) < pxcmStatus.PXCM_STATUS_NO_ERROR) break;
                    ToolStripMenuItem sm1 = new ToolStripMenuItem(dinfo.name.get(), null, new EventHandler(RadioCheck));
                    sm.DropDownItems.Add(sm1);
                }
                capture.Dispose();
            }
            if (sm.DropDownItems.Count > 0)
                (sm.DropDownItems[0] as ToolStripMenuItem).Checked = true;
            menuStrip1.Items.RemoveAt(0);
            menuStrip1.Items.Insert(0, sm);
        }
    

Для создания модуля распознавания речи нам понадобится создать свой класс MyUtilMPipeline.cs, наследника класса SDK UtilMPipeline, который имеет доступ к алгоритмам распознавания голоса. Основными функциями будут OnRecognized и OnAlert, для получения событий распознавания и сигналов предупреждения соответсвенно. Переопределим их под свои нужды.

class MyUtilMPipeline : UtilMPipeline
    {
        protected Form2 f2;

        public void SetForm(Form2 f2)
        {
            this.f2 = f2;
        }
        public override void OnRecognized(ref PXCMVoiceRecognition.Recognition data)
        {
            if (data.label < 0)            //если произнесена не команда
                f2.PrintWord(data.dictation);      //продиктованное предложение      
        }
        public override void OnAlert(ref PXCMVoiceRecognition.Alert data)
        {
            f2.PrintStatus(f2.GetStatus(data.label)); 
        }
    }
    

PXCMVoiceRecognition.Alert data.label может принимать значение из ограниченного множества:

public string GetStatus(PXCMVoiceRecognition.Alert.Label label)
        {
            switch (label)
            {
                case PXCMVoiceRecognition.Alert.Label.LABEL_SNR_LOW: return "Слишком сильный посторонний шум";
                case PXCMVoiceRecognition.Alert.Label.LABEL_SPEECH_UNRECOGNIZABLE: return "Не удалось распознать речь";
                case PXCMVoiceRecognition.Alert.Label.LABEL_VOLUME_HIGH: return "Громкость речи слишком высокая";
                case PXCMVoiceRecognition.Alert.Label.LABEL_VOLUME_LOW: return "Громкость речи слишком низкая";
            }
            return "Error";
        }
    

Чтобы продиктованное слово и соответствующий статус появлялись в treeView на форме Form2 используются функции PrintWord,PrintStatus, которые реализуем с помощью делегатов, похожими на функцию UpdateStatus формы MainForm

private delegate void TreeViewAddDelegate(string word);
        public void PrintWord(string word)
        {
            treeView1.Invoke(new TreeViewAddDelegate(delegate(string word1) { treeView1.Nodes.Add(word1).EnsureVisible(); }), 
            new object[] { word });
        }

        public void PrintStatus(string status)
        {            
            treeView2.Invoke(new TreeViewAddDelegate(delegate(string status1) { treeView2.Nodes.Add(status1); }), 
            new object[] { status });
        }
    

При нажатии на кнопку button1 формы Form2, аналогично кнопке Start формы MainForm, создадим новый поток, в котором будет выполняться функция по распознаванию речи.

private void button1_Click(object sender, EventArgs e)
        {
            button1.Enabled = false;
            button2.Enabled = true;
            menuStrip1.Enabled = false;
            stop = false;
            System.Threading.Thread thread = new System.Threading.Thread(VoiceRecognitionWork);
            thread.Start();
            System.Threading.Thread.Sleep(5);
        }
private delegate void VoiceRecognitionWorkCompleted();
        private void VoiceRecognitionWork()
        {
            voice_recognition.Listen(this);
            this.Invoke(new VoiceRecognitionWorkCompleted(
                delegate
                {
                    button1.Enabled = true;
                    button2.Enabled = false;
                    menuStrip1.Enabled = true;
                    if (closing) Close();
                }
                ));
        }
    

Осталось реализовать метод Listen нового класса voice_recognition.cs, который будет использовать экземпляр класса MyUtilMPipeline для работы с модулем распознавания речи.

class voice_recognition
    {
        public static void Listen(Form2 form2)
        {
            Form2 f2 = form2;            
            PXCMSession session;
            pxcmStatus sts = PXCMSession.CreateInstance(out session);  //создаем сессию         
            if (sts < pxcmStatus.PXCM_STATUS_NO_ERROR)
            {
                f2.PrintStatus("Не удалось загрузить сессию SDK");
                return;
            }

            MyUtilMPipeline myUMP = new MyUtilMPipeline();//создаем экземпляр класса
            myUMP.SetForm(f2); //устанавливаем форму            
            myUMP.QueryCapture().SetFilter(f2.GetSource());//выбираем отмеченное устройство

            //Loading Voice Recognition module
            PXCMSession.ImplDesc desc = new PXCMSession.ImplDesc();
            desc.cuids[0] = PXCMVoiceRecognition.CUID;
            PXCMSession.ImplDesc desc1;
            if (session.QueryImpl(ref desc, 0, out desc1) < pxcmStatus.PXCM_STATUS_NO_ERROR)
            {
                f2.PrintStatus("Не удалось загрузить модуль распознования речи");
                session.Dispose();
                return;
            }                   
          
            myUMP.EnableVoiceRecognition(desc1.friendlyName.get());// включаем распознавание голоса с выбранным устройством

            myUMP.SetProfileIndex(0); //выбираем профиль по умолчанию

            myUMP.SetVoiceDictation();// выбираем режим диктора

            f2.PrintStatus("Иницилизация...");
            if (myUMP.Init())
            {
                myUMP.QueryCapture().device.SetProperty(PXCMCapture.Device.Property.PROPERTY_AUDIO_MIX_LEVEL, 0.3f); // устанавливаем уровень записи микрофона (от 0 до 1)

                while (!f2.IsStop())
                {
                    if (!myUMP.AcquireFrame(true))
                        break;
                    myUMP.ReleaseFrame();
                }
            }
            else
            {
                f2.PrintStatus("Иницилизация закончилась неудачно");
            }

            myUMP.Close();
            myUMP.Dispose();
            f2.PrintStatus("Остановлено");

        }
    }
    

Функция AcquireFrame ждет нового доступного кадра, затем блокирует его для обработки приложением. Приложение должно снять блокировку, вызвав функцию ReleaseFrame. После этого начинается обработка последующих кадров.

Чтобы остановить процесс, необходимо обработать событие нажатия кнопки button2 формы Form2 аналогично кнопки Stop формы MainForm

private void button2_Click(object sender, EventArgs e)
        {
            stop = true;
        }

        public bool IsStop()
        {
            return stop;
        }
    

Продиктованное слово или короткое предложение будет появляться в treeView1.

(рис 4.10)

Теперь необходимо, чтобы при нажатии кнопки button3, выбранное слово сохранялось. В случае создания пользователя передавалось в форму mainmenu, в случае авторизации - на форму Form1.

private void button3_Click(object sender, EventArgs e)
        {
            //проверить на выбранное слово
            if (treeView1.SelectedNode == null)
            {
                MessageBox.Show("Выберите слово из списка распознанных");
                return;
            }
            else
            {
                button2_Click(sender, e); //останавливаем
                //проверяем каким конструктором создан экземпляр формы
                if (f1 == null) //creating
                {
                    mm1.UpdateSecretword(treeView1.SelectedNode.Text);
                }
                else
                { //auth
                    string word = treeView1.SelectedNode.Text;
                    f1.UpdateDataFromVoiceRecognition(word);                
                }    
                stop = true;
                closing = true;
            }
        }
    

Объединение модулей в программе

Последним шагом создания приложения будет завершения процесса создания нового пользователя, путем собрания всех данных и созданием записи в базе данных, а также проверки организация процесса авторизации.

За процесс создания нового пользователя отвечает событие нажатия на кнопку button5 формы mainmenu:

private void button5_Click(object sender, EventArgs e)
        {
            username = textBox1.Text;
            password = textBox2.Text;
            //проверка на наличие букв и добавления кодового слова            
            if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password) || string.IsNullOrWhiteSpace(secretword))
            {
                MessageBox.Show("Заполните все поля и данные");
                return;
            }
            //создадим хэш от пароля и кодового слова, чтобы не хранить их в БД в //открытом виде, а также приведя строки к длине в 44 символа, в независимости от длины //исходной строки
            password = GetSHA256Hash(password);
            secretword = GetSHA256Hash(secretword);
            
            /* Нахождение строки источника данных БД для подключения, указываем ту, что появилась при создании БД.
Альтернативно можно прописать абсолютный путь к БД, например:
conString = @"Data Source=C:\Users\1\Desktop\intel perceptual computing sdk\practice 2\MyDB1.sdf"; */
            string conString = practice_2.cs.Properties.Settings.Default.MyDatabase_1ConnectionString;
            using (SqlCeConnection con = new SqlCeConnection(conString))            
            {
                con.Open();
                var rowId = new SqlCeCommand("SELECT TOP(1) id FROM users ORDER BY Id DESC", con).ExecuteScalar();//выбирем максимыльнай id в таблице
                using (SqlCeCommand com = new SqlCeCommand("INSERT INTO users VALUES(@Id,@Username,@Password,@Secretword)", con))//создадим запись с новым пользователм
                {
                    int newid = Convert.ToInt32(rowId) + 1;                   
                    com.Parameters.AddWithValue("@Id", newid);
                    com.Parameters.AddWithValue("@Username", username);
                    com.Parameters.AddWithValue("@Password", password);
                    com.Parameters.AddWithValue("@Secretword", secretword);
                    com.ExecuteNonQuery();
                }
                label2.Text = username;
            }
            this.Width = 265;
        }

private string GetSHA256Hash(string s) //создания хэша от строки
        {
            if (string.IsNullOrEmpty(s))
            {
                throw new ArgumentException("An empty string value cannot be hashed.");
            }

            Byte[] data = System.Text.Encoding.UTF8.GetBytes(s);
            Byte[] hash = new SHA256CryptoServiceProvider().ComputeHash(data);
            return Convert.ToBase64String(hash);
        }
    

Подключите соответствующие пространства имен

using System.Data.SqlServerCe;
using System.Security.Cryptography;
    

За процесс авторизации отвечает функция обработки события нажатия кнопки button3 формы Form1:

private void button3_Click(object sender, EventArgs e)
        {
            username = textBox1.Text;
            password = textBox2.Text;
            //проверка на получение информации с модуля распознавания лиц и модуля //распознвания голоса
            if (nameFromFaceRegognition == null || wordFromVoiceRegognition == null || password == null || username == "Guest")
            {
                MessageBox.Show("Недостаточно данных для авторизации");
                return;
            }

            password = GetSHA256Hash(password);
            wordFromVoiceRegognition = GetSHA256Hash(wordFromVoiceRegognition);
            string nameFromVoiceRecognition = null;
            /* Нахождение строки источника данных БД для подключения, указываем ту, что появилась при создании БД.
Альтернативно можно прописать абсолютный путь к БД, например:
conString = @"Data Source=C:\Users\1\Desktop\intel perceptual computing sdk\practice 2\MyDB1.sdf"; */
            string conString = practice_2.cs.Properties.Settings.Default.MyDatabase_1ConnectionString;
            using (SqlCeConnection con = new SqlCeConnection(conString))                      {
                con.Open();
          //поиск пользователя на основе пароля и информации с модуля распознавания речи  
                using (SqlCeCommand com1 = new SqlCeCommand("SELECT Username FROM users WHERE Password=@Password AND Secretword=@Secretword", con))
                {                    
                    com1.Parameters.AddWithValue("@Password", password);
                    com1.Parameters.AddWithValue("@Secretword", wordFromVoiceRegognition);
                    SqlCeDataReader reader = com1.ExecuteReader();
                    while (reader.Read())
                    {
                        nameFromVoiceRecognition = reader.GetString(0);                        
                    }
                }
            }
//сравнивание имен пользователей
            if (nameFromFaceRegognition != null  nameFromVoiceRecognition != null)
            {
                if (nameFromFaceRegognition == nameFromVoiceRecognition)
                {
                    textBox1.Text = nameFromFaceRegognition;
                    MessageBox.Show("Вы успешно авторизованы как " + nameFromFaceRegognition);
                    mm1.SetAuthUsername(nameFromFaceRegognition);
                    Close();
                    return;
                }

            }
            MessageBox.Show("Ошибка авторизации. Не верные данные");
        }
    

Дополнительные задания:

  • Введите дополнительный параметр в метод идентификации человека, такой как "уровень соответсвия" (рис 4.11)
  • Введите дополнительную характеристику "Жест" при создании и авторизации пользователя на основе модуля распознавания жестов (рис 4.12)
  • Вернуться к учебному плану