Windows Phone용 카메라 앱에서 회색조 사용하는 방법

2013-12-05

적용 대상: Windows Phone 8 | Windows Phone OS 7.1

 

Windows Phone OS 7.1 부터는 Microsoft.Devices.PhotoCamera 클래스를 사용하여 프로그래밍 방식으로 휴대폰 카메라에 액세스할 수 있습니다. 이 항목에서는 카메라 미리 보기 버퍼에서 라이브 동영상 프레임을 변경하는 방법에 대해 설명합니다. 이 항목에 설명된 앱은 카메라에서 ARGB(알파, 빨강, 녹색, 파랑) 프레임을 처리하고 회색조로 변환하는 방법을 보여 줍니다. 이 항목은 카메라 회색조 샘플에 해당합니다.

팁팁:

Windows Phone 8 앱에서 회색조 프레임을 처리해야 할 경우 GetPreviewBufferY(Byte[]) 메서드를 사용해 보세요. 이 메서드는 효과적인 YCbCr 형식을 사용하여 카메라 미리 보기 버퍼에서 광도(Y) 정보만 캡처합니다. Windows.Phone.Media.Capture.PhotoCaptureDevice 클래스 사용에 대한 자세한 내용은 Windows Phone 8의 고급 사진 캡처를 참조하세요.

이 항목은 두 부분으로 나누어져 있습니다.

중요중요:

Windows Phone OS 7.1 의 기능을 사용하기 위해 Windows Phone OS 7.0 앱을 업그레이드하는 경우 카메라 기능 ID_CAP_ISV_CAMERA는 앱 매니페스트 파일 WMAppManifest.xml에 자동으로 추가되지 않습니다. ID_CAP_ISV_CAMERA가 없으면 카메라 API를 사용하는 앱이 작동하지 않습니다. 새 Windows Phone OS 7.1 프로젝트에서는 이 기능이 앱 매니페스트 파일에 포함되어 있습니다.

다음 이미지는 이 항목에서 만든 카메라 앱을 보여 줍니다.

AP_Con_CameraGrayscale

이 섹션에서는 뷰파인더 영역, 컬러와 회색조 모드 간을 전환하기 위한 버튼 StackPanel 컨트롤, 뷰파인더 영역을 오버레이하는 Image 컨트롤로 구성되는 카메라 UI를 만듭니다.

카메라 UI 및 기본 기능을 만들려면

  1. Windows Phone SDK 를 통해 Windows Phone 앱 템플릿을 사용하여 새 프로젝트를 만듭니다.

  2. 프로젝트를 만든 후 프로젝트 메뉴에서 참조 추가를 선택합니다. .NET 탭에서 Microsoft.XNA.Framework를 선택하고 확인을 클릭합니다.

  3. MainPage.xaml 파일에서 다음 코드에 표시된 대로 phone:PhoneApplicationPage 요소를 업데이트합니다.

        SupportedOrientations="Landscape" Orientation="LandscapeLeft"
        shell:SystemTray.IsVisible="False"
    
    

    이렇게 하면 가로 방향의 페이지가 구성되고 시스템 트레이가 숨겨집니다.

  4. MainPage.xaml에서 이름이 LayoutRootGrid를 다음 코드로 바꿉니다.

        <!--LayoutRoot is the root grid where all page content is placed-->
        <Grid x:Name="LayoutRoot" Background="Transparent">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="640" />
                <ColumnDefinition Width="160*" />
            </Grid.ColumnDefinitions>
    
            <!--Camera viewfinder >-->
            <Rectangle Width="640" Height="480" HorizontalAlignment="Left" >
                <Rectangle.Fill>
                    <VideoBrush x:Name="viewfinderBrush" />
                </Rectangle.Fill>
    
            </Rectangle>
    
            <!--Overlay for the viewfinder region to display grayscale WriteableBitmap objects-->
            <Image x:Name="MainImage" 
                   Width="320" Height="240" 
                   HorizontalAlignment="Left" VerticalAlignment="Bottom"  
                   Margin="16,0,0,16"
                   Stretch="Uniform"/>
    
            <!--Button StackPanel to the right of viewfinder>-->
            <StackPanel Grid.Column="1" >
                <Button             
                    Content="Gray: ON"
                    Name="GrayscaleOnButton"  
                    Click="GrayOn_Clicked" />
                <Button             
                    Content="Gray: OFF"
                    Name="GrayscaleOffButton"  
                    Click="GrayOff_Clicked" />
            </StackPanel>
    
            <!--Used for debugging >-->
            <TextBlock Height="40" HorizontalAlignment="Left" Margin="8,428,0,0" Name="txtDebug" VerticalAlignment="Top" Width="626" FontSize="24" FontWeight="ExtraBold" />
    
        </Grid>
    
    

    이 코드는 회색조 켜기 및 끄기 버튼이 포함될 StackPanel 컨트롤이 있는 640 x 480 뷰파인더 영역을 만듭니다. 다시 말해 Image 컨트롤의 목적은 회색조 모드가 선택되었을 때 뷰파인더를 오버레이하는 것입니다. 프레임 펌프에서 제공되는 회색조 WriteableBitmap 개체에 대한 대체 보기 영역을 만듭니다.

  5. 기본 페이지의 코드 숨김 파일 MainPage.xaml.cs를 열고 페이지 맨 위에 다음 지시문을 추가합니다.

    // Directives
    using Microsoft.Devices;
    using System.Windows.Media.Imaging;
    using System.Threading;
    
    
  6. MainPage.xaml.csMainPage 클래스에서 MainPage 클래스 생성자 앞에 다음 변수 선언을 추가합니다.

        // Variables
        PhotoCamera cam = new PhotoCamera();
        private static ManualResetEvent pauseFramesEvent = new ManualResetEvent(true);
        private WriteableBitmap wb;
        private Thread ARGBFramesThread;
        private bool pumpARGBFrames;
    
    
  7. MainPage.xaml.cs에서 MainPage 클래스에 다음 코드를 추가합니다.

    참고참고:

    이 절차의 다음 단계를 완료할 때까지 Visual Studio에 현재 컨텍스트에 없는 메서드에 대한 오류가 나열될 수 있습니다. 이러한 메서드는 다음 단계에서 추가됩니다.

            //Code for camera initialization event, and setting the source for the viewfinder
            protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
            {
    
                // Check to see if the camera is available on the phone.
                if ((PhotoCamera.IsCameraTypeSupported(CameraType.Primary) == true) ||
                     (PhotoCamera.IsCameraTypeSupported(CameraType.FrontFacing) == true))
                {
                    // Initialize the default camera.
                    cam = new Microsoft.Devices.PhotoCamera();
    
                    //Event is fired when the PhotoCamera object has been initialized
                    cam.Initialized += new EventHandler<Microsoft.Devices.CameraOperationCompletedEventArgs>(cam_Initialized);
    
                    //Set the VideoBrush source to the camera
                    viewfinderBrush.SetSource(cam);
                }
                else
                {
                    // The camera is not supported on the phone.
                    this.Dispatcher.BeginInvoke(delegate()
                    {
                        // Write message.
                        txtDebug.Text = "A Camera is not available on this phone.";
                    });
    
                    // Disable UI.
                    GrayscaleOnButton.IsEnabled = false;
                    GrayscaleOffButton.IsEnabled = false;
                }
            }
    
            protected override void OnNavigatingFrom(System.Windows.Navigation.NavigatingCancelEventArgs e)
            {
                if (cam != null)
                {
                    // Dispose of the camera to minimize power consumption and to expedite shutdown.
                    cam.Dispose();
    
                    // Release memory, ensure garbage collection.
                    cam.Initialized -= cam_Initialized;
                }
            }
    
    

    이 코드는 OnNavigatedTo(NavigationEventArgs) 메서드를 사용하여 cam이라는 PhotoCamera 개체를 만들고 이벤트 처리기를 추가합니다. 또한 이 코드는 VideoBrush 소스를 휴대폰 카메라 개체 cam으로 설정합니다. 휴대폰에서 카메라를 사용할 수 없는 경우 버튼이 사용하지 않도록 설정되고 UI에 메시지가 표시됩니다.

    참고참고:

    카메라에서 동영상 프레임을 가져오려면 GetPreviewBufferArgb32(Int32[]) 메서드를 사용할 때와 마찬가지로 PhotoCamera 개체가 VideoBrush 컨트롤에 동영상 미리 보기를 표시하도록 설정되어야 합니다.

  8. MainPage.xaml.cs에서 MainPage 클래스에 다음 코드를 추가합니다.

        protected override void OnNavigatingFrom(System.Windows.Navigation.NavigatingCancelEventArgs e)
        {
            // Dispose camera to minimize power consumption and to expedite shutdown.
            cam.Dispose();
    
            // Release memory, ensure garbage collection.
            cam.Initialized -= cam_Initialized;
        }
    
    

    이 코드를 통해 카메라에 관련된 메모리를 해제할 수 있습니다.

  9. MainPage.xaml.cs에서 MainPage 클래스에 다음 코드를 추가합니다.

    //Update UI if initialization succeeds
            void cam_Initialized(object sender, Microsoft.Devices.CameraOperationCompletedEventArgs e)
            {        
                if (e.Succeeded)
                {
                    this.Dispatcher.BeginInvoke(delegate()
                    {
                        txtDebug.Text = "Camera initialized";
                    });
                   
                }
            }
    
    

    이 코드는 카메라 Initialized 이벤트를 사용하여 txtDebug라는 TextBlock을 업데이트합니다. 앱 UI는 다른 스레드에서 실행되므로 상태를 업데이트하려면 BeginInvoke 메서드가 필요합니다.

  10. 카메라 앱을 만들려면 앱 매니페스트 파일에서 카메라 기능을 선언해야 합니다. 그렇지 않으면 앱이 작동하지 않습니다. WMAppManifest.xml을 열고 다음 기능 요소가 있는지 확인합니다.

    <Capability Name="ID_CAP_ISV_CAMERA"/>
    

    앱 기능 및 요구 사항에 대한 자세한 내용은 Windows Phone의 앱 기능 및 하드웨어 요구 사항을 참조하세요.

이 섹션에서는 카메라에서 ARGB(알파, 빨강, 녹색, 파랑)를 가져오고 이것을 회색조로 변환하는 과정을 총괄적으로 담당하는 2개 메서드를 만듭니다.

ARGB 프레임 펌프를 만들려면

  1. MainPage.xaml.cs에서 MainPage 클래스에 다음 코드를 추가합니다.

        // ARGB frame pump
        void PumpARGBFrames()
        {
            // Create capture buffer.
            int[] ARGBPx = new int[(int) cam.PreviewResolution.Width * (int) cam.PreviewResolution.Height];
    
            try
            {
                PhotoCamera phCam = (PhotoCamera)cam;
    
                while (pumpARGBFrames)
                {
                    pauseFramesEvent.WaitOne();
    
                    // Copies the current viewfinder frame into a buffer for further manipulation.
                    phCam.GetPreviewBufferArgb32(ARGBPx);
    
                    // Conversion to grayscale.
                    for (int i = 0; i < ARGBPx.Length; i++)
                    {
                        ARGBPx[i] = ColorToGray(ARGBPx[i]);
                    }
    
                    pauseFramesEvent.Reset();
                    Deployment.Current.Dispatcher.BeginInvoke(delegate()
                    {
                        // Copy to WriteableBitmap.
                        ARGBPx.CopyTo(wb.Pixels, 0);
                        wb.Invalidate();
    
                        pauseFramesEvent.Set();
                    });
                }
    
            }
            catch (Exception e)
            {
                this.Dispatcher.BeginInvoke(delegate()
                {
                    // Display error message.
                    txtDebug.Text = e.Message;
                });
            }
        }
    
        internal int ColorToGray(int color)
        {
            int gray = 0;
    
            int a = color >> 24;
            int r = (color & 0x00ff0000) >> 16;
            int g = (color & 0x0000ff00) >> 8;
            int b = (color & 0x000000ff);
    
            if ((r == g) && (g == b))
            {
                gray = color;
            }
            else
            {
                // Calculate for the illumination.
                // I =(int)(0.109375*R + 0.59375*G + 0.296875*B + 0.5)
                int i = (7 * r + 38 * g + 19 * b + 32) >> 6;
    
                gray = ((a & 0xFF) << 24) | ((i & 0xFF) << 16) | ((i & 0xFF) << 8) | (i & 0xFF);
            }
            return gray;
        }
    
    

    이 코드에서 PumpARGBFrames라는 메서드(ARGB 프레임 펌프)는 조작을 위해 ARGB 프레임을 버퍼로 복사합니다. 그런 다음 각 프레임이 버퍼에 있는 동안 ColorToGray 메서드를 사용하여 프레임을 회색조로 변환합니다. 마지막으로 PumpARGBFrames는 변환한 프레임을 wb라는 WriteableBitmap 개체로 복사합니다. 프레임 펌프의 연속 처리는 UI에 표시되는 라이브 흑백 동영상을 생성하는 데 사용됩니다.

    참고참고:

    카메라에서 동영상 프레임을 가져오려면 GetPreviewBufferArgb32(Int32[]) 메서드메서드를 사용할 때와 마찬가지로 PhotoCamera 개체가 VideoBrush 컨트롤에 동영상 미리 보기를 표시하도록 설정되어야 합니다.

  2. MainPage.xaml.cs에서 MainPage 클래스에 다음 코드를 추가합니다.

        // Start ARGB to grayscale pump.
        private void GrayOn_Clicked(object sender, RoutedEventArgs e)
        {
            MainImage.Visibility = Visibility.Visible;
            pumpARGBFrames = true;
            ARGBFramesThread = new System.Threading.Thread(PumpARGBFrames);
    
            wb = new WriteableBitmap((int) cam.PreviewResolution.Width, (int) cam.PreviewResolution.Height);
            this.MainImage.Source = wb;
    
            // Start pump.
            ARGBFramesThread.Start();
            this.Dispatcher.BeginInvoke(delegate()
            {
                txtDebug.Text = "ARGB to Grayscale";
            });
        }
    
        // Stop ARGB to grayscale pump.
        private void GrayOff_Clicked(object sender, RoutedEventArgs e)
        {
            MainImage.Visibility = Visibility.Collapsed;
            pumpARGBFrames = false;
    
            this.Dispatcher.BeginInvoke(delegate()
            {
                txtDebug.Text = "";
            });
        }
    
    

    이 코드에서 GrayOn_Clicked 메서드는 화면이 덮이도록 MainImage라는 Image 컨트롤의 크기를 조정하고 ARGB 프레임 펌프에 의해 업데이트되는 wb라는 쓰기 가능 비트맵을 표시하도록 설정합니다. GrayOff_Clicked 이벤트는 MainImage 컨트롤을 축소하고 프레임 펌프를 중지합니다.

    참고참고:

    Image 컨트롤의 가시성은 특정 상황에서 축소됩니다. 다시 말해 이 컨트롤은 표준 색 뷰파인더 구현을 오버레이합니다. 이 컨트롤은 회색조 보기에서 활성화되고 표시됩니다.

  3. 휴대폰에서 디버그 | 디버깅 시작 메뉴 명령을 선택하여 앱을 실행합니다.

표시:
© 2014 Microsoft