티스토리 뷰

반응형

Home Automation 프로젝트로 기획했던 Home Security Solution에 대한 두번째 시간이다. 인체동작 감지센서(PIR 센서)로 동작 감지시 LCD에 문자를 표시해주는 것을 지난 번에 다루었는데

2016.09.16 - [Technology/Windows 10 IoT] - 인체감지 센서(PIR)값에 따른 LCD Display 하기

 

인체감지 센서(PIR)값에 따른 LCD Display 하기

집앞에 어슬렁거리는 빈집 털이범이나, 여자들만 사는 곳을 노리는 범죄를 예방하기위해 각 아파트마다 CCTV가 설치되어 있다. 다만 그런 CCTV의 경우 자신의 집앞을 정확하게 비쳐줄지모르고 규

hero-space.tistory.com

오늘은 동작이 감지되면 RaspberryPI에 연결되어 있는 Web Cam이 촬영을 시작해서 5초간 촬영하고 촬영한 시간을 파일이름으로 저장하고 바로 UI로 재생시켜주게 해보자.

다시한번 전체 시나리오를 보자.

문 앞에 누군가가 감지되었을때, 카메라를 통해서 촬영하고 낯선 사람일 경우 클라우드로 전송한다.

세부적으로 나누어보면,

1) 인체 감지센서를 통해 문 앞의 사람 인식(완료)

2) 인식 되었을 때, 사진촬영 및 저장

3) 낯선 사람일 경우 클라우드로 전송

# Web Cam 선택

Windows 10 IoT Core 기반에 RaspberryPI에 Web Cam을 통한 촬영 기능을 사용하기 위해선 우선 지원하는 Web Cam을 골라야 한다. Web Cam을 제조하는 회사가 다양하기 때문에 모든 Web Cam을 지원하려면 그에 맞는 Driver를 Windows 10 IoT Core에 다 설치되어 있어야하기때문에 불가능하고 주로 사용되는 Web Cam에 대해서 Windows 10 IoT 공식 홈페이지에서 리스트를 알려주고 있다.

https://developer.microsoft.com/ko-kr/windows/iot/docs/hardwarecompatlist#Cameras

Microsoft 에서 나와는 Web Cam과 Logitech 에서 나온 Web Cam 을 볼수가 있는데 그중에서 저렴한 Microsoft Lifecam 3000 USB Camera를 선택하기로 했다. Spec은 아래와 같으니 구입시 고민하시는 분들은 참조하면 좋을 듯 하다.

그럼 이제 모든 부픔이 준비됬다. 기존에 인체 감지 센서를 통해서 동작이 감지되었을 때, 16X2 사이즈 LCD에 표시해주는데 이어서 촬영하여 저장하고 연결된 UI에 바로 재생되도록 만들어 보겠다.

# Hardware 구성

인체감지 센서의 입력 GPIO 핀은 5번으로 하고 Web Cam, 인체감지 센서 등 동작 준비 완료가 되면 이를 보여주는 것은 LED를 하나 달아서 보여주기 위해 GPIO 핀 12번을 사용한다. 16X2 LCD는 기존 글을 참고하고 Web Cam은 RaspberryPI에 USB로 연결한다.

UI 구성

사진을 보면굉장히 복잡해 보이지만 LCD를 제외하면 간단한 연결이며 쉽게 구성할 수 있다. 이제 본격적으로 프로그램 작성을 해보자.

# Software 작성

LCD 관련한 코드는 LCD.cs 클래스로 별도 분리하고 나머지 메인 프로그램은 MainPage.xaml,cs 파일에 작성하도록 한다. 기본적으로 C# 빈프로젝트를 생성하고 'Windows IoT Extension for the UWP' 확장 SDK를 참조 추가하는 것으로 Software 작성을 시작한다. 기본적인 Code들이 제너레이트 되기 때문에 실제 필요한 부분만 다루도록 할 것이다.

<Web Cam 사용을 위한 manifest 파일 설정하기>

Web Cam의 기능을 사용하기 위해서는 Package.appxmanigest 파일을 오픈하여 기능 tab에 마이크, 비디오라이브러리, 웹캠을 선택하고 저장한다. 이것으로 프로그램에서 Web Cam을 기본적으로 이용할 수 있는 상태가 되었다.

<필요한 라이브러리 추가>

        using System;                               using System.Diagnostics;            // Debug 용         using Windows.UI.Xaml;         using Windows.UI.Xaml.Controls;          using Windows.Media.Capture;                  // 동영상 촬영용         using Windows.Media.MediaProperties;          // 동영상 속성 설정용         using System.Threading;                       // 타이머용         using System.Threading.Tasks;                 // 타이머용         using Windows.Storage;                        // 동영상 파일명         using Windows.Devices.Gpio;                   // GPIO용         using Windows.Devices.Enumeration; 

<프로그램에서 사용할 변수 선언>

        public sealed partial class MainPage : Page         {              private MediaCapture capture;          // 캡쳐 객체              private StorageFile videoFile;         // 동영상 저장용              private string fileName;               // 저장용 파일 이름              private Timer timer;                   // 녹화용 타이머              private bool isRecording = false;      // 녹화용 플래그              private bool isSensor = false;         // 센서용 플래그              private const int inputPin = 5;        // 센서 입력용 GPIO 핀              private const int actPin = 12;         // 동작 확인용 ACTLED              private GpioPin pin;              private GpioPin statusPin;              LCD lcd; 

<Application의 초기화와 이벤트 추가 수행>

        public MainPage()         {              this.InitializeComponent();              initLCD();              Loaded += MainPage_Loaded;         }         private async void MainPage_Loaded(object sender, RoutedEventArgs e)         {             await initWebcam();             await initGpio();         } 

<Web Cam 초기화>

        private async Task initWebcam()         {             Debug.WriteLine("initializing Web Camera...");              try             {                 if (capture != null)                 {                     if (isRecording)                     {                         await capture.StopRecordAsync();                         isRecording = false;                     }                      capture.Dispose();                     capture = null;                 }                 var devices = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);                  if (devices.Count == 0)                 {                     Debug.WriteLine("Not able to find Webcam.");                     return;                 }                  capture = new MediaCapture();                 await capture.InitializeAsync();                  await Task.Delay(10000);                  Debug.WriteLine("Initialized Webcam");             }             catch(Exception ex)             {                 Debug.WriteLine(ex.Message);             }         } 

<GPIO 초기화>

GPIO는 인체동작 감지 센서와 Web Cam과 센서의 초기화가 완료되면 알려주는 LED 를 설정한다.

        private async Task initGpio()         {             var gpio = GpioController.GetDefault();             if (gpio == null)             {                 Debug.WriteLine("Not found GPIO");                 return;             }              // Set GPIO for PIR             pin = gpio.OpenPin(inputPin);              if (pin.IsDriveModeSupported(GpioPinDriveMode.InputPullUp))             {                 pin.SetDriveMode(GpioPinDriveMode.Input);             }             else             {                 pin.SetDriveMode(GpioPinDriveMode.Input);             }              // ACT LED 설정             statusPin = gpio.OpenPin(actPin);             statusPin.Write(GpioPinValue.High);             statusPin.SetDriveMode(GpioPinDriveMode.Output);              Debug.WriteLine("Initiaizing GPIO");              // wait             await Task.Delay(10000);              // 이벤트 등록             pin.ValueChanged += Pin_ValueChanged;              Debug.WriteLine("Initialized GPIO");             statusPin.Write(GpioPinValue.Low); //If all devices are reday, LED should be turned on         } 

<인체 감지센서 Interrupt 동작>

GPIO 핀 초기화 설정에서 인체 감지센서에 해당되는 GPIO Pin 객체의 메쏘드중 ValueChanged를 이용하여 값이 변경되면 호출되도록 function을 매핍한다. GpioPinEdge.RisingEdge가 되었을때 입력 신호가 ON 되었음을 나타내고 GpioPinEdge.FallingEdge가 되었을 때는 입력 신호가 OFF 임을 나타내며 입력 신호가 ON이 되면 녹화를 시작하는 메쏘드를 호출하도록 한다.(startRecording)

        private void Pin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)         {             if (args.Edge == GpioPinEdge.RisingEdge)             {                 Debug.WriteLine("Rise");                 lcd.WriteLine(" ");                 lcd.WriteLine("Detected");                 if (isRecording == false && isSensor == false)                 {                     startRecording();                 }             }              if(args.Edge == GpioPinEdge.FallingEdge)             {                 Debug.WriteLine("Fall");                 lcd.WriteLine(" ");                 lcd.WriteLine("Clear");                 if (isSensor == true)                 {                     isSensor = false;                 }             }         } 

<Web Cam을 이용한 촬영>

촬영시 파일이름은 현재 시간을 기준으로 만들며 mp4 파일로 생성한다. 촬영시에는 UI에 촬영중이라는 Status를 뿌려주게 하며 5초후에 Timer에 의해 Callback 함수가 호출되도록하여 5초간 촬영될 수 있도록 한다.

        private async void startRecording()         {             if (isSensor == false)             {                 //Set filename accoring to current time                 string time = DateTime.Now.ToString("yyyyMMddHHMMss");                 fileName = string.Format("capture_{0}.mp4", time);                 //Setup Recording File                 videoFile = await Windows.Storage.KnownFolders.VideosLibrary.CreateFileAsync(fileName, Windows.Storage.CreationCollisionOption.GenerateUniqueName);                 MediaEncodingProfile recordProfile = null;                 recordProfile = MediaEncodingProfile.CreateMp4(Windows.Media.MediaProperties.VideoEncodingQuality.Auto);                 Debug.WriteLine("Video storage file preparation successful");                 await capture.StartRecordToStorageFileAsync(recordProfile, videoFile);                  isRecording = true;                 isSensor = true;                 Debug.WriteLine("Start Recording");                  var task = this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>                 {                     status.Text = "Start Recording";                  });                  timer = new Timer(TimerCallback, null, 5000, 5000);             }         } 

<Timer Callback>

촬영을 시작한 후 5초후에 Timer Callback이 호출되어 촬영을 멈추고 바로 UI에 Play를 시킨다.

        private async void TimerCallback(object state)         {             try             {                 Debug.WriteLine("TimerCallback");                  if (isRecording == true)                 {                     timer.Dispose();  //Timer 클래스에서 사용중인 모든 리소스를 해지하고 다시 사용할 수 있는 상태로 바뀜                     await capture.StopRecordAsync();                      Debug.WriteLine("Stop Recording");                     Debug.WriteLine("path : " + videoFile.Path);                                     isRecording = false;                                          var task = this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>                     {                        status.Text = "Recorded file and Play";                        PlayVideo();                     });                 }             }             catch(Exception ex)             {                                  if (ex is System.UnauthorizedAccessException)                 {                     Debug.WriteLine("Unable to play recorded video; video recorded successfully to: " + videoFile.Path);                  }                 else                 {                     Debug.WriteLine(ex.Message);                  }                             }                      } 

<Play Video>

        private async void PlayVideo()         {             Debug.WriteLine("Play Video");             if (videoFile.Path != null)             {                // StorageFile playFile = videoFile;                 var stream = await videoFile.OpenReadAsync();                 playbackElement.AutoPlay = true;                 playbackElement.SetSource(stream, videoFile.FileType);                 playbackElement.Play();             }         } 

# 실행

이제 프로그램을 다 작성해 보았다. 실행해서 정상동작을 하는지 살펴보도록 한다.

실행 후 network drive 접속기능을 이용해서 파일을 보면 빨간 사각형 경로를 기본경로로 촬영된 영상이 저장되어 있음을 볼수 있다.

 

 

Home Security 프로젝트를 완료하기 위한 두번째 과정이 마무리 되었다. 다만 촬영을 하더라도 얼굴이 인식을 해서 인식되는 경우 클라우드로 전송하는 기능은 영상처리를 필요로 하기때문에 좀더 복잡해 질 것 같지만 우선은 여기까지~, 코드상에 세세히 설명할 필요가 있는 부분은 차차 다시 다룰 예정이다.~!

 

반응형
댓글