Северный (Арктический) федеральный университет им. М.В. Ломоносова
Опубликован: 23.08.2014 | Доступ: свободный | Студентов: 312 / 55 | Длительность: 11:13:00
Лабораторная работа 5:

Управление встраиваемыми системами с использованием возможностей Intel Perceptual Computing SDK

< Лекция 7 || Лабораторная работа 5

Цель текущей лабораторной работы заключается в использовании возможностей модуля распознавания лиц Intel Perceptual Computing SDK для реализации сервиса сбережения энергии персонального компьютера.

Задачи, которые должны быть решены в ходе работы:

  1. Реализовать способы обнаружения человека перед компьютером:
    • Анализ присутствия человека перед монитором при помощи веб-камеры;
    • Анализ поступающей информации с клавиатуры и мыши (дополнительное задание).
  2. Реализовать способы сбережения энергии:
    • Уменьшение яркости экрана;
    • Переход в спящий режим или гибернацию;
    • Отключение периферийных устройств (сетевой карты).

Необходимо заметить, что прототип приложения для данной практической работы написан на языке C#, соответственно сниппеты будут приведены в синтаксисе C#.

1. Анализ присутствия человека перед монитором

Определять человека на основе изображения с веб-камеры будем, используя возможностей модуля распознавания лиц Intel Perceptual Computing SDK. Для того чтобы использовать возможности SDK нам понадобится создать новый проект C# и добавить в него ссылку на библиотеку libpxcclr.dll, которая находится в папке sdk/bin/x64 или sdk/bin/x86. Также libpxcclr.dll нужно скопировать в папку с исполняемым файлом проекта.

Создадим на главной форме нашего приложения три объекта: текстовое поле textbox1, PictureBox1 размера 640х480 для отображения изображения, а также кнопку button1. Задача сделать, чтобы при нажатии кнопки на PictureBox1 появлялось изображение с веб-камеры, в котором обнаруживалось лицо человека, если оно найдено.

Для того, чтобы изображение с веб-камеры непрерывно отображалось в реальном времени, и мы могли параллельно вести работу с нашей формой (нажимать на другие кнопки, вводить значения и т.д.), обработку и вывод изображения нужно сделать в отдельном потоке. Для этого воспользуемся классом С# BackgroundWorker, который имеет два требуемых события: DoWork (где будет браться изображение с веб-камеры и анализироваться) и ProgressChanged (где будет отображаться изменения - вывод изображение на форму). Создадим область кода внутри формы, где будем описывать переменные, связанные с веб-камерой. Для начала, поместим туда объект типа background.

#region webcam
BackgroundWorker background = new BackgroundWorker();
#endregion
    

Также обработаем событие при появлении формы (Shown), в котором добавим обработчики события.


private void Form1_Shown(object sender, EventArgs e)
        {           
            background.DoWork += new DoWorkEventHandler(background_DoWork);
            background.WorkerReportsProgress = true;            
            background.ProgressChanged += new ProgressChangedEventHandler(background_ProgressChanged);
        }
    

Функции background_DoWork и background_ProgressChanged рассмотрим позже.

Первое, что необходимо сделать при работе с SDK, это создать сессию. Для этого нам понадобится объект PXCMSession (далее сессия). Любая операция с SDK возвращает статус типа PxcmStatus. Назовем этот объект sts. Он содержит код успеха или ошибки, который берется из некоторого множества. Как только сессия создана, необходимо захватить кадр с веб-камеры. Для этого необходим класс UtilMCapture. Назовем этот объект capture. Так как SDK поддерживает несколько типов захвата данных из разных потоков (RGB изображение, инфракрасное изображения, звук и т.д.), то при создании сессии необходим профиль, чтобы захватывать данные из нужного потока. Для распознавания лиц на изображении необходимо подключить базовый модуль распознавания лиц типа PXCMBase, который является частью интерфейса PXCFaceAnalysis. После того как сессия успешно создана, необходимо создать профиль в интерфейсе PXCFaceAnalysis типа ProfileInfo, который будет хранить результат запросов к модулю распознавания лиц. Этот модуль поддерживает обнаружение лиц с помощью типа PXCMFaceAnalysis.Detection, для которого также нужно создать профиль, где будут храниться результаты от запросов. Добавим новые переменные в область webcam:

#region webcam
        ...
        PXCMSession session;
        UtilMCapture capture;        
        pxcmStatus sts;
        PXCMBase fanalysis;
        PXCMFaceAnalysis fa;
        PXCMFaceAnalysis.ProfileInfo pf = new PXCMFaceAnalysis.ProfileInfo();
        PXCMFaceAnalysis.Detection detection;
        PXCMFaceAnalysis.Detection.ProfileInfo dinfo;
    

Рассмотрим функцию нажатия на кнопку button1, в которой создадим сессию и настроим профили.

private void button1_Click(object sender, EventArgs e)
        {
                try
                {    
               	// создаем сессию
                    sts = PXCMSession.CreateInstance(out session); 
                    if (sts < pxcmStatus.PXCM_STATUS_NO_ERROR)
                    {
                        textBox1.Text = "Failed to create the SDK session";
                        return;
                    }

                    //загружаем модуль распознования лиц
                    sts = session.CreateImpl(PXCMFaceAnalysis.CUID, out fanalysis); 
                    if (sts < pxcmStatus.PXCM_STATUS_NO_ERROR)
                    {
                        textBox1.Text = "Failed to load face recognition module";
                        session.Dispose();
                        return;
                    }
			//создаем профиль
                    fa = (PXCMFaceAnalysis)fanalysis.DynamicCast(PXCMFaceAnalysis.CUID);
                    fa.QueryProfile(0, out pf);
			// нахождение устройства захвата кадров  
                    capture = new UtilMCapture(session);
                    sts = capture.LocateStreams(ref pf.inputs);
                    if (sts < pxcmStatus.PXCM_STATUS_NO_ERROR)
                    {
                        textBox1.Text = "Failed to locate a capture module";
                        capture.Dispose();
                        session.Dispose();
                        return;
                    }
			// настраиваем профиль
                    fa.SetProfile(ref pf);

			// создаем профиль обнаружения лиц
                    detection = (PXCMFaceAnalysis.Detection)fa.DynamicCast(PXCMFaceAnalysis.Detection.CUID);
                    dinfo = new PXCMFaceAnalysis.Detection.ProfileInfo();
                    detection.QueryProfile(0, out dinfo);
                    // настраиваем профиль обнаружения лиц
                    detection.SetProfile(ref dinfo);
                    // запускаем отдельный поток (начинается выполнение функции background_DoWork)
                    background.RunWorkerAsync();               
                }
                catch
                {
			//освобождаем ресурсы, завершаем процесс
                    capture.Dispose();
                    session.Dispose();
                    MessageBox.Show("Error Creating Session! Application Will exit");
			Process.GetCurrentProcess().Kill();
		   }   
        }
    
Листинг .

Функция background_DoWork() представляет собой бесконечный цикл, в котором изображение с веб-камеры берется из видео потока и анализируется на наличие лиц. Вначале мы захватываем кадр, используя интерфейс изображений. Его нужно объявить с помощью (добавить в область webcam):

PXCMImage[] images = new PXCMImage[PXCMCapture.VideoStream.STREAM_LIMIT];
PXCMScheduler.SyncPoint[] sps = new PXCMScheduler.SyncPoint[2];
    

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

void background_DoWork(object sender, DoWorkEventArgs e)
        {
            
            BackgroundWorker bgr = sender as BackgroundWorker;
            pf.iftracking = true;
            for (int nframes = 0; ; ) //бесконечный цикл
            {
                try
                {
			// захватываем кадр
                    sts = capture.ReadStreamAsync(images, out sps[0]);
                    if (sts < pxcmStatus.PXCM_STATUS_NO_ERROR)
                    {                        
                        textBox1.Text = "Device failed\n";
                        break;
                    }
			//обрабатываем кадр с помощью модуля обнаружения лиц
                    sts = fa.ProcessImageAsync(new PXCMImage[] { images[0] }, out sps[1]);
                    PXCMScheduler.SyncPoint.SynchronizeEx(sps);
			//запрашиваем изображение, сохраняя его в Bitmap bmp                    
                    images[0].QueryBitmap(session, out bmp);          
                    
                    //запускаем функцию Background_ProgressChanged(для отображения изображения)                 
                    bgr.ReportProgress(0);                    
			//освободжаем ресурсы
                    System.Threading.Thread.Sleep(20);
                    
                    foreach (PXCMScheduler.SyncPoint s in sps) if (s != null) s.Dispose();
                    foreach (PXCMImage i in images) if (i != null) i.Dispose();                    
                }
                catch
                {
                    MessageBox.Show("Frame capturing Error!");
                    return;
                }
            }
	     //освобождаем ресурсы       
            fa.Dispose();
            capture.Dispose();
            session.Dispose();
            bgr.ReportProgress(100);
        }
    
Листинг .

Событие ProgressChanged класса BackgroundWorker возникает при вызове метода ReportProgress и необходимо для того, чтобы сообщить о ходе выполнения фоновой операции. Также во время этого события можно взаимодействовать с пользовательским интерфейсом (в DoWork это недоступно).

Для того чтобы найти лица на изображении необходимо воспользоваться функцией QueryFace интерфейса PXCMFaceAnalysis в результате получим идентификатор лица, затем используя этот идентификатор, мы запрашиваем данные с помощью запроса QueryData, которые включают в себя координаты прямоугольника, окружающего лицо. Зная эти координаты, мы можем дорисовать прямоугольник на наш bitmap bmp, который уже хранит изображение с веб-камеры.

void background_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {            
            try
            {
                if (sps[0].Synchronize(0) >= pxcmStatus.PXCM_STATUS_NO_ERROR)
                {
                    ulong timeStamp; // время
                    int fid; // идентификатор лица
                    uint fidx = 0; // ищем первое найденное лицо на кадре.
                    fa.QueryFace(fidx, out fid, out timeStamp);                    

                    PXCMFaceAnalysis.Detection.Data face_data;
                    detection.QueryData(fid, out face_data);
                    
                    if (face_data.rectangle.h > 0)
                    {                         
                            using (Graphics g = Graphics.FromImage(bmp))
                            {
                                Rec1.X = (int)(face_data.rectangle.x);
                                Rec1.Y = (int)(face_data.rectangle.y);
                                Rec1.Width = (int)(face_data.rectangle.w);
                                Rec1.Height = (int)(face_data.rectangle.h);
                                this.Invoke((MethodInvoker)delegate
                                {
                                    g.DrawRectangle(P1, Rec1);                                
                                });
                            }
                    }
                }

                if (pictureBox1.Image != null)
                    pictureBox1.Image.Dispose();
                if (bmp != null)
                {
                    pictureBox1.Height = bmp.Height;
                    pictureBox1.Width = bmp.Width;
                    pictureBox1.Image = bmp;
                }
            }
            catch (Exception ex)
            {
                pictureBox1.Image = null; 
            }            
        }
    
Листинг .

В этой функции мы используем объекты Pen и Rectangle для рисования прямоугольника, используя класс Graphics, поэтому необходимо их заранее объявить в области webcam.

#region webcam
. . .
Bitmap bmp = new Bitmap(480,640);
Pen P1 = new Pen(new SolidBrush(Color.Green), 6);
Rectangle Rec1 = new Rectangle(0, 0, 0, 0);
#endregion
    

Анализ поступающей информации с клавиатуры и мыши (дополнительное задание)

Для анализа движения мыши или ввода с клавиатуры вам необходимо использовать Hooks (от англ. "hook" - крюк) - механизм обработки сообщений системы, с помощью которого приложение может перехватывать события, такие как действия мыши и нажатия клавиш. Функция, которая перехватывает события определенного типа, называется процедурой-обработчиком. Процедура-обработчик может быть создана для любого события и, срабатывая, изменять или отменять события. Рассмотрим, как создать такие механизмы для перехвата сообщений на действия мыши и нажатия клавиш.

2. Способы энергосбережения

Все методы сохранения энергии программно можно выполнять с помощью c# пространства имен System.Management, которое предоставляет средства доступа к обширному набору сведений и событий управления, относящихся к системе, устройствам и приложениям, поддерживающим структуру WMI (Windows Management Instrumentation - инструментарий управления Windows). Приложения и службы могут запрашивать важные сведения об управлении, например, о яркости экрана, об объеме свободного места на диске или о подключенных устройствах, с помощью классов, производных от ManagementObjectSearcher и ManagmentQuery.

Яркость экрана

Самый эффективный способ уменьшить потребление энергии это уменьшить яркость экрана. Следующий способ не работает на стационарных компьютерах, так как они не имеют необходимой поддержки на аппаратном уровне.

Добавим на нашу форму элемент trackbar. Наша задача, чтобы при изменении ползунка изменялась яркость экрана. Добавим в наш проект новый класс под названием "ScreenBrightness". Будем использовать WMI класс WmiMonitorBrightness, который имеет нужные нам свойства:

Uint8 CurrentBrightness - текущая яркость экрана, uint8 Level[] - массив, который хранит уровни яркости в процентах (заметим, что эти уровни могут различаться на разных компьютерах, где-то это массив может состоять из 7 элементов, а где-то из 10).

Для начала, нам необходимо уметь получать эти уровни яркости. Воспользуемся классом ManagementObjectSearcher. Создадим функцию GetBrightnessLevels в нашем новом классе, которая будет возвращать массив значений:

public static byte[] GetBrightnessLevels()
        {            
            //определим где искать
            System.Management.ManagementScope s = new System.Management.ManagementScope("root\\WMI");

            //определим запрос
            System.Management.SelectQuery q = new System.Management.SelectQuery("WmiMonitorBrightness");

            //найдем объекты
            System.Management.ManagementObjectSearcher mos = new System.Management.ManagementObjectSearcher(s, q);
            byte[] BrightnessLevels = new byte[0];          
	     //получим объекты
            System.Management.ManagementObjectCollection moc = mos.Get();

            //сохраним результат
            foreach (System.Management.ManagementObject o in moc)
            {
                BrightnessLevels = (byte[])o.GetPropertyValue("Level");
                break; //работает только для первого объекта
            }
	     //освобождаем ресурсы
            moc.Dispose();
            mos.Dispose();

            return BrightnessLevels;
        }
        

Теперь необходимо создать функцию, которая будет изменять яркость экрана, создадим функцию SetBrightness, которая похожа по структуре на GetBrightnessLevels():

public delegate void SetBrightness_Delegate(byte targetBrightness);

        public static void SetBrightness(byte targetBrightness)
        {
            // определим где искать
            System.Management.ManagementScope s = new System.Management.ManagementScope("root\\WMI");

            // определим запрос
            System.Management.SelectQuery q = new System.Management.SelectQuery("WmiMonitorBrightnessMethods");

            // найдем объекты
            System.Management.ManagementObjectSearcher mos = new System.Management.ManagementObjectSearcher(s, q);
	     // получим объекты
            System.Management.ManagementObjectCollection moc = mos.Get();
	     //изменим яркость
            foreach (System.Management.ManagementObject o in moc)
            {
                o.InvokeMethod("WmiSetBrightness", new Object[] { UInt32.MaxValue, targetBrightness });
                break; //работает только для первого объекта
            }
	     //освобождаем ресурсы
            moc.Dispose();
            mos.Dispose();
            
        }
    }
        

Теперь осталось, чтобы при загрузке нашей формы, trackbar показывал текущую яркость экрана. Добавим функцию, которая возвращает текущую яркость экрана:

public static int GetBrightness()
        {
            // определим где искать
            System.Management.ManagementScope s = new System.Management.ManagementScope("root\\WMI");

            // определим запрос
            System.Management.SelectQuery q = new System.Management.SelectQuery("WmiMonitorBrightness");

            // найдем объекты
            System.Management.ManagementObjectSearcher mos = new System.Management.ManagementObjectSearcher(s, q);
	     // получим объекты
            System.Management.ManagementObjectCollection moc = mos.Get();

            //сохраним результат
            byte curBrightness = 0;
            foreach (System.Management.ManagementObject o in moc)
            {
                curBrightness = (byte)o.GetPropertyValue("CurrentBrightness");
                break; //работает только для первого объекта
            }
	     // освобождаем ресурсы
            moc.Dispose();
            mos.Dispose();

            return (int)curBrightness;
        }
        

Чтобы при открытии формы trackbar показывал текущий уровень яркости, добавим в функцию private void Form1_Shown(object sender, EventArgs e) следующие строчки:

byte[] bLevels;
bLevels = ScreenBrightness.GetBrightnessLevels();
            if (bLevels.Count() == 0)
                MessageBox.Show("Sorry, Your System does not support this brightness control");
            else
            {
                trackBar1.TickFrequency = bLevels.Count();
                trackBar1.Maximum = bLevels.Count() - 1;
                trackBar1.Update();
                trackBar1.Refresh();

                int CurBrightness = ScreenBrightness.GetBrightness();
                int pos = Array.IndexOf(bLevels, (byte)CurBrightness);
                if (pos < 0)
                    pos = 1;
                trackBar1.Value = pos;
            }
        

Последнее, нужно добавить в обработчик события trackbar_Scroll внесения изменения яркости:

private void trackBar1_Scroll(object sender, EventArgs e)
        {
            ScreenBrightness.SetBrightness(bLevels[trackBar1.Value]);            
        }
        

Отключение устройств

Отключение некоторых устройств, таких как: неиспользующиеся жесткие диски, сетевой адаптер, также может снизить потребление энергии компьютера. Реализуем в нашем проекте включение и отключение сетевых подключений (например wi-fi). Добавим в наш проект новый класс с названием "Network_Adapter". В нем будет всего две функции - включение и отключение сетевого адаптера. В дальнейшем этот класс можно расширить, добавив в него функции по отключению других устройств. Для реализации функций воспользуемся теме же методами, что и при изменении яркости - классом ManagementObjectSearcher.

public static void Network_Adapter_Disable(ref string T)
        {
	     //определяем где искать
            System.Management.ManagementScope s = new System.Management.ManagementScope("root\\cimv2");            
            //определяем запрос
            System.Management.SelectQuery q = new System.Management.SelectQuery("select * from win32_networkadapter where Name="+"'"+T+"'");
            // найдем объекты
            System.Management.ManagementObjectSearcher mos = new System.Management.ManagementObjectSearcher(s, q);
	     //получим объекты
            System.Management.ManagementObjectCollection moc = mos.Get();
            foreach (System.Management.ManagementObject o in moc)
            {                  
                o.InvokeMethod("Disable",null,null);
                break;
             }
	      //освобождаем ресурсы
             moc.Dispose();
             mos.Dispose();              
       }
        

Функция для включения сетевого адаптера отличается только одной строчкой.

o.InvokeMethod("Disable",null,null);
        

поменять на:

o.InvokeMethod("Enable",null,null);
        

Также необходимо создать делегаты этих функций для их вызова из главной формы:

public delegate void NetworkAdapterOn_Delegate(ref string T);
public delegate void NetworkAdapterOff_Delegate(ref string T);
        

Добавим на главную форму еще одну кнопку button2 и элемент listview. Пусть при открытии формы в listview показывается список всех сетевых адаптеров, а при нажатии на кнопку, выбранный адаптер отключался. Изменим свойства "view" элемента listview1 на "details", а свойство "checkboxes" на "true". Добавим колонку с заголовком "network adapters" и создадим функцию Get_Device_list().

private void Get_Device_list()
        {
            System.Management.ManagementScope s = new System.Management.ManagementScope("root\\cimv2");

            //определим запрос (доступные сетевые адаптеры, кроме виртуальных)
           System.Management.SelectQuery q = new System.Management.SelectQuery(@"SELECT * 
                                     FROM   Win32_NetworkAdapter 
                                     WHERE  Manufacturer != 'Microsoft' 
                                            AND (ConfigManagerErrorCode = 0 
                                                    OR (ConfigManagerErrorCode = 22 AND NetConnectionStatus = 0))");
            
            System.Management.ManagementObjectSearcher mos = new System.Management.ManagementObjectSearcher(s, q);

            System.Management.ManagementObjectCollection moc = mos.Get();

            foreach (System.Management.ManagementObject o in moc)
            {
                string ss = o.GetPropertyValue("Name").ToString();
                listView1.Items.Add(ss);                
            }
            moc.Dispose();
            mos.Dispose();
        }
        

Добавим эту функцию в обработчик события Form1_Shown. Тогда при нажатии на кнопку button2 должны выполняться следующие действия:

private void button2_Click(object sender, EventArgs e)
        {            
            foreach (ListViewItem checkedItem in listView1.Columns[0].ListView.CheckedItems)
            {                
  Network_Adapter.NetworkAdapterOff_Delegate caller = Network_Adapter.Network_Adapter_Disable;
                IAsyncResult Async_result = caller.BeginInvoke(ref  checkedItem.Text.ToString(),null, null);
                caller.EndInvoke(ref checkedItem.Text.ToString(),Async_result);

            }
           
        }
        

Таким образом, отключение устройства будет происходить асинхронно.

Заключение

На основе классов и функций, реализованных в этом проекте, можно создать приложение, которое с эффективностью может уменьшать энергопотребление компьютера. Используя методы обнаружения присутствия человека перед компьютером, реализуются функции, которые, при отсутствии человека перед компьютером, по истечению некоторого времени (заданного пользователем) запускают методы сбережения электроэнергии. Как только приложение получает сигнал о том, что пользователь обнаружен, все настройки восстанавливаются.

< Лекция 7 || Лабораторная работа 5
Дмитрий Юнушкин
Дмитрий Юнушкин

В лабораторной работе №2 (идентификация лица) сказано:

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

Класса MyUtilMPipeline  нет в проекте вообще;

Функции AdvancedPipeline так же нет. Материалов к лабораторной  №2 в начале работы (по ссылке открывается та же страница) тоже нет.Это ошибки или используется другая версия примера?