정보
요청한 주제가 아래에 표시됩니다. 그러나 이 주제는 이 라이브러리에 포함되지 않습니다.

Windows Phone 8용 기본 카메라 앱을 만드는 방법

2014-06-18

적용 대상: Windows Phone 8 및 Windows Phone Silverlight 8.1 | Windows Phone OS 7.1

 

Windows Phone OS 7.1 부터는 Microsoft.Devices.PhotoCamera 클래스를 사용하여 프로그래밍 방식으로 휴대폰 카메라에 액세스할 수 있습니다. 이 항목은 사진 촬영을 위해 이 API를 사용하는 것을 보여 주는 카메라 앱을 만드는 방법을 설명하는 시리즈의 첫 번째 부분입니다. 이 항목에서는 카메라 뷰파인더를 표시하고, 프로그래밍 방식으로 셔터를 트리거하고, 촬영한 이미지를 미디어 라이브러리 및 로컬 폴더에 저장하는 방법에 대해 설명합니다.

Windows Phone 8 부터 앱에서는 고급 캡처 시나리오에 대해 PhotoCaptureDevice 클래스를 사용할 수 있습니다. PhotoCaptureDevice를 사용하여 ISO, 노출 보정 및 수동 초점 위치(휴대폰에서 사용 가능한 경우) 등의 사진 속성을 제어할 수 있습니다. 이 항목에서는 PhotoCaptureDevice에 대해 설명하지 않습니다. 자세한 내용은 Windows Phone 8의 고급 사진 캡처를 참조하세요.

참고참고:

multiple devices 사진 캡처가 앱의 핵심 기능이 아니라면 카메라 API를 직접 사용하는 대신 카메라 캡처 작업을 사용하여 사진을 캡처하도록 고려해 보세요. 카메라 캡처 작업에 할당되는 메모리는 총 앱 메모리 사용에 포함되지 않습니다. 그러면 앱의 메모리 사용도 최소화되며, 이는 앱이 저메모리 단말기 에서 실행되는 경우 특히 중요합니다. 자세한 내용은 Windows Phone 8 저메모리 휴대폰용 앱 개발Windows Phone 8의 카메라 캡처 작업을 사용하는 방법을 참조하세요.

이 항목에서는 다음 단계에 대해 설명합니다.

  1. 카메라 UI 만들기

  2. 뷰파인더 및 카메라 기반 이벤트 구현

  3. 미디어 라이브러리 및 로컬 폴더에 저장

팁팁:

이 항목은 기본 카메라 샘플에 해당합니다.

이 기본 카메라 앱을 완성한 후 그 다음 항목에는 플래시 및 초점 구현, 촬영 해상도 변경 및 휴대폰 하드웨어 셔터 버튼 사용 등의 기능이 포함됩니다. 계속 진행하려면 먼저 이 항목에 나오는 기본 카메라 앱을 완성해야 합니다. 그 다음 항목은 이 앱을 기반으로 구성되며 이 항목에서 구현된 특성에 기능을 추가합니다.

이러한 모든 항목을 완료하고 나면 완성된 솔루션이 다음 이미지와 같이 표시됩니다.

AP_Con_CameraAnatomy

이 항목에서 SH 버튼만 앱에 추가됩니다. 플래시, 자동 초점, 해상도 버튼은 이 시리즈의 다른 항목에서 추가됩니다.

참고참고:

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 프로젝트에서는 이 기능이 앱 매니페스트 파일에 포함되어 있습니다.

이 항목에서는 Windows Phone OS 7.1 에뮬레이터(Emulator 7.5)로 완료할 수 없습니다. 휴대폰 또는 Windows Phone 8 에뮬레이터로 이 앱을 개발하세요.

이 섹션에서는 촬영된 프레임을 표시할 뷰파인더 영역과 이미지를 촬영할 셔터 버튼으로 구성된 카메라 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>
            
            <Canvas x:Name="viewfinderCanvas" Width="640" Height="480" 
                       HorizontalAlignment="Left" >
                
                <!--Camera viewfinder -->
                <Canvas.Background>
                    <VideoBrush x:Name="viewfinderBrush" />
                </Canvas.Background>
             </Canvas>
            
            <!--Button StackPanel to the right of viewfinder>-->
            <StackPanel Grid.Column="1" >
                <Button x:Name="ShutterButton" Content="SH" Click="ShutterButton_Click" FontSize="26" FontWeight="ExtraBold" Height="75" />
            </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>
    
    

    이 코드는 셔터 버튼 SH를 포함하는 StackPanel 컨트롤을 사용하여 640x480 뷰파인더 영역을 만듭니다. 다음 섹션에서 ShutterButton_Click 이벤트를 구현합니다.

    참고참고:

    이 연습에서 소프트웨어 셔터 버튼은 단지 PhotoCamera API에 대한 프로그래밍 방식의 액세스를 보여 주기 위해서만 사용됩니다. 사용자의 환경을 최적화하려면 앱이 카메라의 하드웨어 셔터 버튼을 사용하는 것이 좋습니다. 하드웨어 셔터 버튼을 구현하는 방법에 대한 자세한 내용은 Windows Phone 8에서 하드웨어 카메라 셔터 버튼에 액세스하는 방법를 참조하세요.

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

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

        // Variables
        private int savedCounter = 0;
        PhotoCamera cam;
        MediaLibrary library = new MediaLibrary();
    
    
  7. 카메라 앱을 만들려면 앱 매니페스트 파일에서 카메라 기능을 선언해야 합니다. 그렇지 않으면 앱이 작동하지 않습니다. WMAppManifest.xml을 열고 다음 기능 요소가 있는지 확인합니다.

    <Capability Name="ID_CAP_ISV_CAMERA"/>
    

    Windows Phone 8 앱에 후면 카메라가 필요한 경우 ID_REQ_BACKCAMERA 하드웨어 요구 사항을 사용하여 후면 카메라가 없으면 사용자가 앱을 다운로드하지 못하도록 합니다. 기능 및 요구 사항에 대한 자세한 내용은 Windows Phone 8의 앱 기능 및 하드웨어 요구 사항을 참조하세요.

  8. 미디어 라이브러리에 액세스하기 위해서는 앱 매니페스트 파일에서 미디어 라이브러리 기능을 선언해야 합니다. 그렇지 않으면 앱에서 파일을 미디어 라이브러리에 저장할 수 없게 됩니다. 앱이 대상으로 하는 Windows Phone 의 버전에 따라 다음 기능을 추가하세요.

    • ID_CAP_MEDIALIB: Windows Phone OS 7.1 앱은 이 기능을 사용하여 미디어 라이브러리에 액세스합니다.

    • ID_CAP_MEDIALIB_PHOTO: Windows Phone 8 앱은 이 기능을 사용하여 사진을 미디어 라이브러리에 저장합니다.

    Windows Phone 8 앱은 추가적인 기능을 사용하여 미디어 라이브러리의 동영상 및 오디오에 액세스해야 합니다. 자세한 내용은 Windows Phone 8의 앱 기능 및 하드웨어 요구 사항을 참조하세요.

  9. (선택 사항) 앱 기능이 전면 카메라가 필요한 경우 앱이 사용되는 Windows Phone 버전에 따라 앱 매니페스트 파일에 전면 카메라 기능 또는 하드웨어 요구 사항도 추가합니다.

    • ID_HW_FRONTCAMERA: Windows Phone OS 7.1 앱에 이 기능을 사용할 수 있습니다. 이 기능을 통해 전면 카메라가 없는 사용자에게 앱의 일부 기능이 휴대폰에서 작동하지 않지만 앱을 다운로드할 수 있다는 알림이 표시됩니다.

    • ID_REQ_FRONTCAMERA: Windows Phone 8 앱에 이 하드웨어 요구 사항을 사용할 수 있습니다. 이 요구 사항에 따라 전면 카메라가 없는 사용자는 앱을 다운로드할 수 없습니다.

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

뷰 파인더를 구현하려면 viewfinderBrush 소스를 Windows Phone 카메라로 설정합니다. 또한 카메라 초기화, 촬영 완료, 이미지 가용성 등, 여러 카메라 기반 이벤트를 구현합니다.

뷰파인더 및 카메라 기반 이벤트를 구현하려면

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

    참고참고:

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

            //Code for initialization, capture completed, image availability events; also 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 camera, when available.
                    if (PhotoCamera.IsCameraTypeSupported(CameraType.FrontFacing))
                    {
                        // Use front-facing camera if available.
                        cam = new Microsoft.Devices.PhotoCamera(CameraType.FrontFacing);
                    }
                    else
                    {
                        // Otherwise, use standard camera on back of phone.
                        cam = new Microsoft.Devices.PhotoCamera(CameraType.Primary);
                    }
    
                    // Event is fired when the PhotoCamera object has been initialized.
                    cam.Initialized += new EventHandler<Microsoft.Devices.CameraOperationCompletedEventArgs>(cam_Initialized);
    
                    // Event is fired when the capture sequence is complete.
                    cam.CaptureCompleted += new EventHandler<CameraOperationCompletedEventArgs>(cam_CaptureCompleted);
    
                    // Event is fired when the capture sequence is complete and an image is available.
                    cam.CaptureImageAvailable += new EventHandler<Microsoft.Devices.ContentReadyEventArgs>(cam_CaptureImageAvailable);
    
                    // Event is fired when the capture sequence is complete and a thumbnail image is available.
                    cam.CaptureThumbnailAvailable += new EventHandler<ContentReadyEventArgs>(cam_CaptureThumbnailAvailable);
    
                    //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.
                    ShutterButton.IsEnabled = false;
                }
            }
            protected override void OnNavigatingFrom(System.Windows.Navigation.NavigatingCancelEventArgs e)
            {
                if (cam != null)
                {
                    // Dispose camera to minimize power consumption and to expedite shutdown.
                    cam.Dispose();
    
                    // Release memory, ensure garbage collection.
                    cam.Initialized -= cam_Initialized;
                    cam.CaptureCompleted -= cam_CaptureCompleted;
                    cam.CaptureImageAvailable -= cam_CaptureImageAvailable;
                    cam.CaptureThumbnailAvailable -= cam_CaptureThumbnailAvailable;
                }
            }
    
    

    이 코드는 OnNavigatedTo(NavigationEventArgs) 메서드를 사용하여 cam이라는 PhotoCamera 개체를 만들고 여러 이벤트를 구성합니다. 또한 이 코드는 VideoBrush 소스를 휴대폰 카메라 개체 cam으로 설정합니다.

    휴대폰에 기본 카메라 외에 셀프 카메라가 있는 경우 앱은 셀프 카메라를 사용합니다. 모든 휴대폰에 셀프 카메라가 있는 것은 아니므로 PhotoCamera 개체를 만들기 전에 사용 가능 여부를 확인하기 위해 IsCameraTypeSupported(CameraType) 메서드가 사용됩니다. 휴대폰에 어떤 유형의 카메라도 없는 경우도 있습니다. 이 경우 UI를 사용할 수 없게 되고 메시지가 표시됩니다.

    Windows Phone 실행 모델을 설명하기 위해, PhotoCamera 개체는 OnNavigatedTo(NavigationEventArgs) 메서드에서 초기화되고 OnNavigatingFrom(NavigatingCancelEventArgs) 메서드가 실행되는 동안 명시적으로 삭제됩니다. PhotoCamera 이벤트의 이벤트 처리기는 OnNavigatedTo(NavigationEventArgs)에서 추가되고 메모리 확보를 위해 OnNavigatingFrom(NavigatingCancelEventArgs)에서 제거됩니다.

    참고참고:

    카메라 미리 보기 버퍼에서 개별 프레임을 분석하고 처리하는 방법에 대한 내용은 Windows Phone 8용 카메라 앱에서 회색조를 사용하는 방법을 참조하세요.

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

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

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

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

            // Ensure that the viewfinder is upright in LandscapeRight.
            protected override void OnOrientationChanged(OrientationChangedEventArgs e)
            {
                if (cam != null)
                {
                    // LandscapeRight rotation when camera is on back of phone.
                    int landscapeRightRotation = 180;
    
                    // Change LandscapeRight rotation for front-facing camera.
                    if (cam.CameraType == CameraType.FrontFacing) landscapeRightRotation = -180;
    
                    // Rotate video brush from camera.
                    if (e.Orientation == PageOrientation.LandscapeRight)
                    {
                        // Rotate for LandscapeRight orientation.
                        viewfinderBrush.RelativeTransform =
                            new CompositeTransform() { CenterX = 0.5, CenterY = 0.5, Rotation = landscapeRightRotation };
                    }
                    else
                    {
                        // Rotate for standard landscape orientation.
                        viewfinderBrush.RelativeTransform =
                            new CompositeTransform() { CenterX = 0.5, CenterY = 0.5, Rotation = 0 };
                    }
                }
    
                base.OnOrientationChanged(e);
            }
    
    

    이 코드는 카메라가 거꾸로(LandscapeRight 방향) 되어 있을 때 뷰파인더 viewfinderBrush가 똑바로 있게 합니다. 카메라가 사용자를 향해 있으면 휴대폰 뒤쪽에 있을 때와는 반대 방향으로 해당 브러시가 회전해야 합니다.

    참고참고:

    동영상 브러시를 180도 회전하면 방향에 상관없이 결과가 동일하지만, 이 코드는 카메라 유형에 따라 UI 방향을 맞추는 방법을 보여 주는 예제를 제공합니다.

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

            private void ShutterButton_Click(object sender, RoutedEventArgs e)
            {
                if (cam != null)
                {
                    try
                    {
                        // Start image capture.
                        cam.CaptureImage();
                    }
                    catch (Exception ex)
                    {
                        this.Dispatcher.BeginInvoke(delegate()
                        {
                            // Cannot capture an image until the previous capture has completed.
                            txtDebug.Text = ex.Message;
                        });
                    }
                }
            }
    
        void cam_CaptureCompleted(object sender, CameraOperationCompletedEventArgs e)
        {
            // Increments the savedCounter variable used for generating JPEG file names.
            savedCounter++;
        }
    
    

    이 코드는 셔터 버튼 및 촬영 완료 이벤트를 구현합니다. 셔터 버튼은 XAML 코드로 만든 소프트웨어 버튼으로, 정지 이미지를 촬영합니다. CaptureCompleted 이벤트는 이 프로젝트에서 savedCounter 변수를 증가시키기 위해 사용되며, 다음 섹션에서는 JPEG 명명을 위해 사용됩니다.

사진을 찍을 때 두 개의 이미지가 찍힙니다. 하나는 최대 해상도 이미지이고, 다른 하나는 갤러리 뷰에서 최대 해상도 사진을 불러오는 데 사용되는 축소판 이미지입니다. 이 섹션에서는 최대 해상도 이미지를 휴대폰 미디어 라이브러리에 넣는 방법에 대해 설명합니다. 또한 최대 해상도 이미지와 축소판 이미지를 로컬 폴더에 저장하는 방법에 대해서도 설명합니다.

이미지를 미디어 라이브러리 및 로컬 폴더에 저장하려면

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

        // Informs when full resolution photo has been taken, saves to local media library and the local folder.
        void cam_CaptureImageAvailable(object sender, Microsoft.Devices.ContentReadyEventArgs e)
        {
            string fileName = savedCounter + ".jpg";
    
            try
            {   // Write message to the UI thread.
                Deployment.Current.Dispatcher.BeginInvoke(delegate()
                {
                    txtDebug.Text = "Captured image available, saving photo.";
                });
    
                // Save photo to the media library camera roll.
                library.SavePictureToCameraRoll(fileName, e.ImageStream);
    
                // Write message to the UI thread.
                Deployment.Current.Dispatcher.BeginInvoke(delegate()
                {
                    txtDebug.Text = "Photo has been saved to camera roll.";
    
                });
    
                // Set the position of the stream back to start
                e.ImageStream.Seek(0, SeekOrigin.Begin);
    
                // Save photo as JPEG to the local folder.
                using (IsolatedStorageFile isStore = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    using (IsolatedStorageFileStream targetStream = isStore.OpenFile(fileName, FileMode.Create, FileAccess.Write))
                    {
                        // Initialize the buffer for 4KB disk pages.
                        byte[] readBuffer = new byte[4096];
                        int bytesRead = -1;
    
                        // Copy the image to the local folder. 
                        while ((bytesRead = e.ImageStream.Read(readBuffer, 0, readBuffer.Length)) > 0)
                        {
                            targetStream.Write(readBuffer, 0, bytesRead);
                        }
                    }
                }
    
                // Write message to the UI thread.
                Deployment.Current.Dispatcher.BeginInvoke(delegate()
                {
                    txtDebug.Text = "Photo has been saved to the local folder.";
    
                });
            }
            finally
            {
                // Close image stream
                e.ImageStream.Close();
            }
    
        }
    
        // Informs when thumbnail photo has been taken, saves to the local folder
        // User will select this image in the Photos Hub to bring up the full-resolution. 
        public void cam_CaptureThumbnailAvailable(object sender, ContentReadyEventArgs e)
        {
            string fileName = savedCounter + "_th.jpg";
    
            try
            {
                // Write message to UI thread.
                Deployment.Current.Dispatcher.BeginInvoke(delegate()
                {
                    txtDebug.Text = "Captured image available, saving thumbnail.";
                });
    
                // Save thumbnail as JPEG to the local folder.
                using (IsolatedStorageFile isStore = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    using (IsolatedStorageFileStream targetStream = isStore.OpenFile(fileName, FileMode.Create, FileAccess.Write))
                    {
                        // Initialize the buffer for 4KB disk pages.
                        byte[] readBuffer = new byte[4096];
                        int bytesRead = -1;
    
                        // Copy the thumbnail to the local folder. 
                        while ((bytesRead = e.ImageStream.Read(readBuffer, 0, readBuffer.Length)) > 0)
                        {
                            targetStream.Write(readBuffer, 0, bytesRead);
                        }
                    }
                }
    
                // Write message to UI thread.
                Deployment.Current.Dispatcher.BeginInvoke(delegate()
                {
                    txtDebug.Text = "Thumbnail has been saved to the local folder.";
    
                });
            }
            finally
            {
            // Close image stream
            e.ImageStream.Close();
            }
        }
    
    

    이 코드는 CaptureImageAvailableCaptureThumbnailAvailable 이벤트를 구현합니다. 첫 번째 메서드는 최대 해상도 이미지를 미디어 라이브러리와 로컬 폴더에 저장하고, 두 번째 메서드는 축소판 이미지를 로컬 폴더에 저장하는 방법을 나타냅니다.

    참고참고:

    사진 허브의 저장된 사진 앨범에 사진을 저장하려면 SavePicture() 메서드를 사용합니다. 이 예제에 사용된 SavePictureToCameraRoll() 메서드는 카메라 앨범에 이미지를 저장합니다.

  2. 휴대폰에서 디버그 | 디버깅 시작 메뉴 명령을 선택하여 앱을 실행합니다. SH 버튼을 눌러 앱을 테스트합니다. txtDebugTextBlock은 로컬 폴더에 대한 저장 작업의 상태를 나타냅니다. 앱을 종료한 후 사진 허브의 카메라 앨범 폴더에서 앱으로 찍은 사진을 찾을 수 있습니다.

    다음 예제는 이 시점에서 UI를 표시하는 방법에 대해 설명합니다.

    AP_Con_CameraWireFrame

    이 예제에서 소프트웨어 셔터 버튼 SH는 UI의 오른쪽 상단에 표시되고, 망고 사진은 viewfinderCanvas라는 Canvas 컨트롤에 표시됩니다.

  3. 이제 기본 카메라 앱이 완성되었습니다. 이 앱을 사용하여 다음 항목으로 진행할 수 있습니다.

표시: