다음을 통해 공유


Xamarin.iOS의 수동 카메라 컨트롤

iOS 8에서 제공하는 AVFoundation Framework 수동 카메라 컨트롤을 사용하면 모바일 애플리케이션이 iOS 디바이스의 카메라를 완전히 제어할 수 있습니다. 이 세분화된 수준의 컨트롤을 사용하여 전문적인 수준의 카메라 애플리케이션을 만들고 스틸 이미지 또는 비디오를 촬영하면서 카메라의 매개 변수를 조정하여 아티스트 컴퍼지션을 제공할 수 있습니다.

이러한 컨트롤은 결과가 이미지의 정확성이나 아름다움에 맞게 덜 조정되고 촬영 중인 이미지의 일부 기능 또는 요소를 강조 표시하기 위해 더 많이 조정되는 과학 또는 산업 애플리케이션을 개발할 때 유용할 수도 있습니다.

AVFoundation 캡처 개체

iOS 디바이스에서 카메라를 사용하여 비디오 또는 스틸 이미지를 촬영하든 관계없이 해당 이미지를 캡처하는 데 사용되는 프로세스는 거의 동일합니다. 이는 기본 자동화된 카메라 컨트롤을 사용하는 애플리케이션 또는 새 수동 카메라 컨트롤을 활용하는 애플리케이션의 경우입니다.

AVFoundation 캡처 개체 개요

입력은 . AVCaptureDeviceInput AVCaptureSession 의 방법으로 AVCaptureConnection가져옵니다. 결과는 스틸 이미지 또는 비디오 스트림으로 출력됩니다. 전체 프로세스는 .에 AVCaptureDevice의해 제어됩니다.

제공된 수동 컨트롤

애플리케이션은 iOS 8에서 제공하는 새 API를 사용하여 다음 카메라 기능을 제어할 수 있습니다.

  • 수동 포커스 – 최종 사용자가 포커스를 직접 제어할 수 있도록 함으로써 애플리케이션에서 촬영한 이미지를 보다 자세히 제어할 수 있습니다.
  • 수동 노출 – 노출에 대한 수동 제어를 제공하여 애플리케이션은 사용자에게 더 많은 자유를 제공하고 스타일화된 모양을 구현할 수 있도록 할 수 있습니다.
  • 수동 화이트 밸런스 – 화이트 밸런스는 이미지의 색을 조정하는 데 사용되며, 사실적으로 보이게 하는 경우가 많습니다. 광원의 색 온도가 다르며 이미지를 캡처하는 데 사용되는 카메라 설정이 이러한 차이를 보정하도록 조정됩니다. 다시 말하지만, 사용자가 화이트 밸런스를 제어할 수 있게 함으로써 사용자는 자동으로 수행할 수 없는 조정을 수행할 수 있습니다.

iOS 8은 이미지 캡처 프로세스에 대한 세분화된 제어를 제공하기 위해 기존 iOS API에 대한 확장 및 향상된 기능을 제공합니다.

요구 사항

이 문서에 제시된 단계를 완료하려면 다음이 필요합니다.

  • Xcode 7+ 및 iOS 8 이상 – Apple의 Xcode 7 및 iOS 8 이상 API를 설치하고 개발자 컴퓨터에 구성해야 합니다.
  • Mac용 Visual Studio – 최신 버전의 Mac용 Visual Studio 사용자 디바이스에 설치하고 구성해야 합니다.
  • iOS 8 디바이스 – iOS 8의 최신 버전을 실행하는 iOS 디바이스입니다. iOS 시뮬레이터에서는 카메라 기능을 테스트할 수 없습니다.

일반 AV 캡처 설정

iOS 디바이스에서 비디오를 녹화할 때 항상 필요한 몇 가지 일반적인 설정 코드가 있습니다. 이 섹션에서는 iOS 디바이스의 카메라에서 비디오를 녹화하고 해당 비디오를 실시간으로 UIImageView표시하는 데 필요한 최소 설정을 설명합니다.

출력 샘플 버퍼 대리자

가장 먼저 필요한 것 중 하나는 샘플 출력 버퍼를 모니터링하고 버퍼에서 UIImageView 애플리케이션 UI의 이미지를 표시하는 대리자입니다.

다음 루틴은 샘플 버퍼를 모니터링하고 UI를 업데이트합니다.

using System;
using Foundation;
using UIKit;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Linq;
using AVFoundation;
using CoreVideo;
using CoreMedia;
using CoreGraphics;

namespace ManualCameraControls
{
    public class OutputRecorder : AVCaptureVideoDataOutputSampleBufferDelegate
    {
        #region Computed Properties
        public UIImageView DisplayView { get; set; }
        #endregion

        #region Constructors
        public OutputRecorder ()
        {

        }
        #endregion

        #region Private Methods
        private UIImage GetImageFromSampleBuffer(CMSampleBuffer sampleBuffer) {

            // Get a pixel buffer from the sample buffer
            using (var pixelBuffer = sampleBuffer.GetImageBuffer () as CVPixelBuffer) {
                // Lock the base address
                pixelBuffer.Lock (0);

                // Prepare to decode buffer
                var flags = CGBitmapFlags.PremultipliedFirst | CGBitmapFlags.ByteOrder32Little;

                // Decode buffer - Create a new colorspace
                using (var cs = CGColorSpace.CreateDeviceRGB ()) {

                    // Create new context from buffer
                    using (var context = new CGBitmapContext (pixelBuffer.BaseAddress,
                        pixelBuffer.Width,
                        pixelBuffer.Height,
                        8,
                        pixelBuffer.BytesPerRow,
                        cs,
                        (CGImageAlphaInfo)flags)) {

                        // Get the image from the context
                        using (var cgImage = context.ToImage ()) {

                            // Unlock and return image
                            pixelBuffer.Unlock (0);
                            return UIImage.FromImage (cgImage);
                        }
                    }
                }
            }
        }
        #endregion

        #region Override Methods
        public override void DidOutputSampleBuffer (AVCaptureOutput captureOutput, CMSampleBuffer sampleBuffer, AVCaptureConnection connection)
        {
            // Trap all errors
            try {
                // Grab an image from the buffer
                var image = GetImageFromSampleBuffer(sampleBuffer);

                // Display the image
                if (DisplayView !=null) {
                    DisplayView.BeginInvokeOnMainThread(() => {
                        // Set the image
                        if (DisplayView.Image != null) DisplayView.Image.Dispose();
                        DisplayView.Image = image;

                        // Rotate image to the correct display orientation
                        DisplayView.Transform = CGAffineTransform.MakeRotation((float)Math.PI/2);
                    });
                }

                // IMPORTANT: You must release the buffer because AVFoundation has a fixed number
                // of buffers and will stop delivering frames if it runs out.
                sampleBuffer.Dispose();
            }
            catch(Exception e) {
                // Report error
                Console.WriteLine ("Error sampling buffer: {0}", e.Message);
            }
        }
        #endregion
    }
}

이 루틴을 사용하면 AppDelegate AV 캡처 세션을 열어 라이브 비디오 피드를 기록하도록 수정할 수 있습니다.

AV 캡처 세션 만들기

AV 캡처 세션은 iOS 디바이스의 카메라에서 라이브 비디오 녹화를 제어하는 데 사용되며 비디오를 iOS 애플리케이션으로 가져오는 데 필요합니다. 예제 ManualCameraControl 애플리케이션은 여러 위치에서 캡처 세션을 사용하므로 전체 애플리케이션에서 AppDelegate 구성되고 사용할 수 있게 됩니다.

애플리케이션 AppDelegate 을 수정하고 필요한 코드를 추가하려면 다음을 수행합니다.

  1. 솔루션 탐색기 파일을 두 번 클릭하여 AppDelegate.cs 편집용으로 엽니다.

  2. 파일 맨 위에 다음 using 문을 추가합니다.

    using System;
    using Foundation;
    using UIKit;
    using System.CodeDom.Compiler;
    using System.Collections.Generic;
    using System.Linq;
    using AVFoundation;
    using CoreVideo;
    using CoreMedia;
    using CoreGraphics;
    using CoreFoundation;
    
  3. 클래스에 다음 프라이빗 변수 및 계산 속성을 AppDelegate 추가합니다.

    #region Private Variables
    private NSError Error;
    #endregion
    
    #region Computed Properties
    public override UIWindow Window {get;set;}
    public bool CameraAvailable { get; set; }
    public AVCaptureSession Session { get; set; }
    public AVCaptureDevice CaptureDevice { get; set; }
    public OutputRecorder Recorder { get; set; }
    public DispatchQueue Queue { get; set; }
    public AVCaptureDeviceInput Input { get; set; }
    #endregion
    
  4. 완료된 메서드를 재정의하고 다음으로 변경합니다.

    public override void FinishedLaunching (UIApplication application)
    {
        // Create a new capture session
        Session = new AVCaptureSession ();
        Session.SessionPreset = AVCaptureSession.PresetMedium;
    
        // Create a device input
        CaptureDevice = AVCaptureDevice.DefaultDeviceWithMediaType (AVMediaType.Video);
        if (CaptureDevice == null) {
            // Video capture not supported, abort
            Console.WriteLine ("Video recording not supported on this device");
            CameraAvailable = false;
            return;
        }
    
        // Prepare device for configuration
        CaptureDevice.LockForConfiguration (out Error);
        if (Error != null) {
            // There has been an issue, abort
            Console.WriteLine ("Error: {0}", Error.LocalizedDescription);
            CaptureDevice.UnlockForConfiguration ();
            return;
        }
    
        // Configure stream for 15 frames per second (fps)
        CaptureDevice.ActiveVideoMinFrameDuration = new CMTime (1, 15);
    
        // Unlock configuration
        CaptureDevice.UnlockForConfiguration ();
    
        // Get input from capture device
        Input = AVCaptureDeviceInput.FromDevice (CaptureDevice);
        if (Input == null) {
            // Error, report and abort
            Console.WriteLine ("Unable to gain input from capture device.");
            CameraAvailable = false;
            return;
        }
    
        // Attach input to session
        Session.AddInput (Input);
    
        // Create a new output
        var output = new AVCaptureVideoDataOutput ();
        var settings = new AVVideoSettingsUncompressed ();
        settings.PixelFormatType = CVPixelFormatType.CV32BGRA;
        output.WeakVideoSettings = settings.Dictionary;
    
        // Configure and attach to the output to the session
        Queue = new DispatchQueue ("ManCamQueue");
        Recorder = new OutputRecorder ();
        output.SetSampleBufferDelegate (Recorder, Queue);
        Session.AddOutput (output);
    
        // Let tabs know that a camera is available
        CameraAvailable = true;
    }
    
  5. 변경 내용을 파일에 저장합니다.

이 코드를 사용하면 수동 카메라 컨트롤을 실험 및 테스트용으로 쉽게 구현할 수 있습니다.

수동 포커스

최종 사용자가 포커스를 직접 제어할 수 있도록 함으로써 애플리케이션은 촬영한 이미지에 대한 더 많은 예술적 제어를 제공할 수 있습니다.

예를 들어 전문 사진작가는 이미지의 포커스를 부드럽게 하여 보케 효과를 얻을 수 있습니다. 또는 포커스 끌어오기 효과를 만듭니다.

과학자 또는 의료 응용 프로그램의 작가에 대 한, 응용 프로그램 프로그래밍 방식으로 실험에 대 한 주위에 렌즈를 이동 할 수 있습니다. 어떤 방식으로든 새 API를 사용하면 최종 사용자 또는 애플리케이션이 이미지를 촬영할 때 포커스를 제어할 수 있습니다.

포커스 작동 방식

IOS 8 애플리케이션에서 포커스를 제어하는 세부 정보를 논의하기 전에. iOS 디바이스에서 포커스가 작동하는 방식을 간략하게 살펴보겠습니다.

iOS 디바이스에서 포커스가 작동하는 방식

광원은 iOS 디바이스의 카메라 렌즈에 들어가고 이미지 센서에 초점을 맞췄습니다. 센서에서 렌즈의 거리는 센서와 관련하여 초점(이미지가 가장 선명하게 표시되는 영역)이 있는 위치를 제어합니다. 렌즈가 센서에서 멀리 떨어져 있으면 거리 개체가 가장 선명해 보이고 가까이 있는 물체가 가장 선명해 보입니다.

iOS 디바이스에서 렌즈는 자석과 스프링에 의해 센서에 더 가깝거나 더 멀리 이동합니다. 그 결과, 렌즈의 정확한 위치는 장치마다 다르며 디바이스의 방향이나 장치 및 스프링의 나이와 같은 매개 변수의 영향을 받을 수 있으므로 불가능합니다.

중요 포커스 용어

포커스를 처리할 때 개발자가 숙지해야 하는 몇 가지 용어가 있습니다.

  • 필드 의 깊이 - 가장 가까운 포커스 내 개체와 가장 먼 포커스 개체 사이의 거리입니다.
  • 매크로 - 포커스 스펙트럼의 가까운 끝이며 렌즈가 집중할 수 있는 가장 가까운 거리입니다.
  • 무한대 – 포커스 스펙트럼의 맨 끝이며 렌즈가 초점을 맞출 수 있는 가장 먼 거리입니다.
  • 하이퍼포컬 거리 – 프레임에서 가장 먼 개체가 포커스의 맨 끝에 있는 포커스 스펙트럼의 지점입니다. 즉, 필드 깊이를 최대화하는 초점 위치입니다.
  • 렌즈 위치 – 위의 다른 모든 용어를 제어하는 것입니다. 이것은 센서에서 렌즈의 거리이며, 그로 인하여 초점의 컨트롤러입니다.

이러한 용어와 지식을 염두에 두고 새로운 수동 포커스 컨트롤을 iOS 8 애플리케이션에서 성공적으로 구현할 수 있습니다.

기존 포커스 컨트롤

iOS 7 및 이전 버전에서는 다음과 같이 속성을 통해 FocusMode기존 포커스 컨트롤을 제공했습니다.

  • AVCaptureFocusModeLocked – 포커스가 단일 포커스 지점에서 잠깁니다.
  • AVCaptureFocusModeAutoFocus – 카메라가 날카로운 포커스를 찾은 다음 그 곳에 머무를 때까지 렌즈를 모든 초점으로 쓸어 줍니다.
  • AVCaptureFocusModeContinuousAutoFocus – 카메라가 포커스가 없는 상태를 감지할 때마다 다시 초점을 맞춥니다.

또한 기존 컨트롤은 사용자가 탭하여 특정 영역에 집중할 수 있도록 속성을 통해FocusPointOfInterest 설정 가능한 관심 지점을 제공했습니다. 또한 애플리케이션은 속성을 모니터링하여 렌즈 움직임을 추적할 IsAdjustingFocus 수 있습니다.

또한 범위 제한은 다음과 같이 속성에 AutoFocusRangeRestriction 의해 제공되었습니다.

  • AVCaptureAutoFocusRangeRestrictionNear – 자동 초점을 주변 깊이로 제한합니다. QR 코드 또는 바코드 검색과 같은 상황에서 유용합니다.
  • AVCaptureAutoFocusRangeRestrictionFar – 자동 초점을 먼 깊이로 제한합니다. 관련이 없는 것으로 알려진 개체가 보기 필드(예: 창 프레임)에 있는 경우에 유용합니다.

마지막으로 SmoothAutoFocus 자동 포커스 알고리즘의 속도를 늦추고 비디오를 녹화할 때 아티팩트를 이동하지 않도록 작은 단위로 단계를 수행하는 속성이 있습니다.

iOS 8의 새로운 포커스 컨트롤

iOS 7 이상에서 이미 제공하는 기능 외에도 이제 다음 기능을 사용하여 iOS 8에서 포커스를 제어할 수 있습니다.

  • 포커스를 잠글 때 렌즈 위치를 완전히 수동으로 제어합니다.
  • 모든 포커스 모드에서 렌즈 위치를 키-값으로 관찰합니다.

위의 기능을 AVCaptureDevice 구현하기 위해 클래스는 카메라 렌즈의 현재 위치를 가져오는 데 사용되는 읽기 전용 LensPosition 속성을 포함하도록 수정되었습니다.

렌즈 위치를 수동으로 제어하려면 캡처 장치가 잠긴 포커스 모드에 있어야 합니다. 예시:

CaptureDevice.FocusMode = AVCaptureFocusMode.Locked;

SetFocusModeLocked 캡처 디바이스의 방법은 카메라 렌즈의 위치를 조정하는 데 사용됩니다. 변경 내용이 적용될 때 알림을 받으려면 선택적 콜백 루틴을 제공할 수 있습니다. 예시:

ThisApp.CaptureDevice.LockForConfiguration(out Error);
ThisApp.CaptureDevice.SetFocusModeLocked(Position.Value,null);
ThisApp.CaptureDevice.UnlockForConfiguration();

위의 코드에서 볼 수 있듯이 렌즈 위치를 변경하려면 먼저 구성을 위해 캡처 디바이스를 잠가야 합니다. 유효한 Lens 위치 값은 0.0에서 1.0 사이입니다.

수동 포커스 예제

일반 AV 캡처 설정 코드가 준비 UIViewController 되면 애플리케이션의 Storyboard에 추가하고 다음과 같이 구성할 수 있습니다.

UIViewController를 애플리케이션 Storyboard에 추가하고 수동 포커스 예제에 대해 여기에 표시된 대로 구성할 수 있습니다.

뷰에는 다음과 같은 주요 요소가 포함됩니다.

  • 비디오 피드를 표시하는 A UIImageView 입니다.
  • UISegmentedControl 포커스 모드를 자동에서 잠김으로 변경하는 A입니다.
  • 현재 렌즈 위치를 표시하고 업데이트하는 A UISlider 입니다.

수동 포커스 컨트롤에 대한 뷰 컨트롤러를 연결하려면 다음을 수행합니다.

  1. 다음 using 명령문을 추가합니다.

    using System;
    using Foundation;
    using UIKit;
    using System.CodeDom.Compiler;
    using System.Collections.Generic;
    using System.Linq;
    using AVFoundation;
    using CoreVideo;
    using CoreMedia;
    using CoreGraphics;
    using CoreFoundation;
    using System.Timers;
    
  2. 다음 프라이빗 변수를 추가합니다.

    #region Private Variables
    private NSError Error;
    private bool Automatic = true;
    #endregion
    
  3. 다음 계산 속성을 추가합니다.

    #region Computed Properties
    public AppDelegate ThisApp {
        get { return (AppDelegate)UIApplication.SharedApplication.Delegate; }
    }
    public Timer SampleTimer { get; set; }
    #endregion
    
  4. 메서드를 재정의 ViewDidLoad 하고 다음 코드를 추가합니다.

    public override void ViewDidLoad ()
    {
        base.ViewDidLoad ();
    
        // Hide no camera label
        NoCamera.Hidden = ThisApp.CameraAvailable;
    
        // Attach to camera view
        ThisApp.Recorder.DisplayView = CameraView;
    
        // Create a timer to monitor and update the UI
        SampleTimer = new Timer (5000);
        SampleTimer.Elapsed += (sender, e) => {
            // Update position slider
            Position.BeginInvokeOnMainThread(() =>{
                Position.Value = ThisApp.Input.Device.LensPosition;
            });
        };
    
        // Watch for value changes
        Segments.ValueChanged += (object sender, EventArgs e) => {
    
            // Lock device for change
            ThisApp.CaptureDevice.LockForConfiguration(out Error);
    
            // Take action based on the segment selected
            switch(Segments.SelectedSegment) {
            case 0:
                // Activate auto focus and start monitoring position
                Position.Enabled = false;
                ThisApp.CaptureDevice.FocusMode = AVCaptureFocusMode.ContinuousAutoFocus;
                SampleTimer.Start();
                Automatic = true;
                break;
            case 1:
                // Stop auto focus and allow the user to control the camera
                SampleTimer.Stop();
                ThisApp.CaptureDevice.FocusMode = AVCaptureFocusMode.Locked;
                Automatic = false;
                Position.Enabled = true;
                break;
            }
    
            // Unlock device
            ThisApp.CaptureDevice.UnlockForConfiguration();
        };
    
        // Monitor position changes
        Position.ValueChanged += (object sender, EventArgs e) => {
    
            // If we are in the automatic mode, ignore changes
            if (Automatic) return;
    
            // Update Focus position
            ThisApp.CaptureDevice.LockForConfiguration(out Error);
            ThisApp.CaptureDevice.SetFocusModeLocked(Position.Value,null);
            ThisApp.CaptureDevice.UnlockForConfiguration();
        };
    }
    
  5. 메서드를 재정의 ViewDidAppear 하고 다음을 추가하여 보기가 로드되면 기록을 시작합니다.

    public override void ViewDidAppear (bool animated)
    {
        base.ViewDidAppear (animated);
    
        // Start udating the display
        if (ThisApp.CameraAvailable) {
            // Remap to this camera view
            ThisApp.Recorder.DisplayView = CameraView;
    
            ThisApp.Session.StartRunning ();
            SampleTimer.Start ();
        }
    }
    
  6. 자동 모드에서 카메라를 사용하면 카메라가 포커스를 조정하면 슬라이더가 자동으로 이동합니다.

    카메라가 이 샘플 앱에서 포커스를 조정하면 슬라이더가 자동으로 이동합니다.

  7. 잠긴 세그먼트를 탭하고 위치 슬라이더를 끌어 렌즈 위치를 수동으로 조정합니다.

    렌즈 위치 수동 조정

  8. 애플리케이션을 중지합니다.

위의 코드에서는 카메라가 자동 모드일 때 렌즈 위치를 모니터링하거나 슬라이더를 사용하여 잠긴 모드에 있을 때 렌즈 위치를 제어하는 방법을 보여 줍니다.

수동 노출

노출은 원본 밝기를 기준으로 이미지의 밝기를 의미하며, 센서에 얼마나 많은 빛이 닿는지, 얼마나 오래, 센서의 게인 레벨(ISO 매핑)에 의해 결정됩니다. 애플리케이션은 노출에 대한 수동 제어를 제공하여 최종 사용자에게 더 많은 자유를 제공하고 스타일화된 모양을 구현할 수 있도록 할 수 있습니다.

수동 노출 컨트롤을 사용하여 사용자는 비현실적으로 밝은 이미지에서 어둡고 우울한 이미지로 이미지를 찍을 수 있습니다.

비현실적으로 밝은 것에서 어둡고 우울한 것까지의 노출을 보여주는 샘플 이미지

다시 말하지만, 이 작업은 과학 애플리케이션에 대한 프로그래밍 방식 컨트롤을 사용하거나 애플리케이션 사용자 인터페이스에서 제공하는 수동 컨트롤을 통해 자동으로 수행할 수 있습니다. 어느 쪽이든 새로운 iOS 8 노출 API는 카메라의 노출 설정에 대한 세분화된 제어를 제공합니다.

노출 작동 방식

IOS 8 애플리케이션에서 노출을 제어하는 세부 정보를 논의하기 전에. 노출의 작동 방식을 간단히 살펴보겠습니다.

노출 작동 방식

노출을 제어하기 위해 함께 제공되는 세 가지 기본 요소는 다음과 같습니다.

  • 셔터 속도 – 카메라 센서에 빛을 비추기 위해 셔터가 열리는 시간입니다. 셔터가 열리는 시간이 짧을수록 빛이 적게 들어오고 이미지가 선명해집니다(동작 흐림이 적음). 셔터가 오래 열릴수록 더 많은 빛이 들어오고 더 많은 동작이 흐려집니다.
  • ISO 매핑 – 이 용어는 필름 사진에서 차용한 용어로, 필름의 화학 물질이 빛에 민감하다는 것을 의미합니다. 필름의 ISO 값이 낮을수록 곡물과 미세한 색 재생이 줄어듭니다. 디지털 센서의 낮은 ISO 값은 센서 노이즈가 적지만 밝기는 낮습니다. ISO 값이 높을수록 이미지가 밝아지지만 센서 노이즈가 더 깁니다. 디지털 센서의 "ISO"는 물리적 기능이 아니라 전자적 이득척도입니다.
  • 렌즈 조리개 – 렌즈 여는 크기입니다. 모든 iOS 장치에서 렌즈 조리개가 고정되어 있으므로 노출을 조정하는 데 사용할 수 있는 값은 셔터 속도와 ISO뿐입니다.

연속 자동 노출의 작동 방식

수동 노출의 작동 방식을 배우기 전에 iOS 디바이스에서 지속적인 자동 노출이 작동하는 방식을 이해하는 것이 좋습니다.

iOS 디바이스에서 연속 자동 노출이 작동하는 방식

첫 번째는 자동 노출 블록이며, 이상적인 노출을 계산하는 작업을 가지고 있으며 계량 통계를 지속적으로 공급하고 있습니다. 이 정보를 사용하여 ISO와 셔터 속도의 최적 혼합물을 계산하여 장면을 잘 조명합니다. 이 주기를 AE 루프라고 합니다.

잠긴 노출의 작동 방식

다음으로, iOS 디바이스에서 잠긴 노출이 작동하는 방식을 살펴보겠습니다.

iOS 디바이스에서 잠긴 노출이 작동하는 방식

다시 말하지만, 최적의 iOS 및 기간 값을 계산하는 자동 노출 블록이 있습니다. 그러나 이 모드에서 AE 블록은 계량 통계 엔진과 연결이 끊어집니다.

기존 노출 컨트롤

iOS 7 이상에서는 속성을 통해 다음과 같은 기존 노출 컨트롤을 ExposureMode 제공합니다.

  • AVCaptureExposureModeLocked – 장면을 한 번 샘플링하고 장면 전체에서 해당 값을 사용합니다.
  • AVCaptureExposureModeContinuousAutoExposure – 장면을 지속적으로 샘플링하여 조명이 제대로 켜져 있는지 확인합니다.

ExposurePointOfInterest 노출할 대상 개체를 선택하여 장면을 노출하기 위해 탭하는 데 사용할 수 있으며, 애플리케이션은 속성을 모니터링 AdjustingExposure 하여 노출이 조정되는 시기를 확인할 수 있습니다.

iOS 8의 새 노출 컨트롤

iOS 7 이상에서 이미 제공하는 기능 외에도 이제 다음 기능을 사용하여 iOS 8에서 노출을 제어할 수 있습니다.

  • 완전 수동 사용자 지정 노출입니다.
  • 가져오기, 설정 및 키 값은 IOS 및 셔터 속도(기간)를 관찰합니다.

위의 기능을 구현하기 위해 새 AVCaptureExposureModeCustom 모드가 추가되었습니다. 카메라가 사용자 지정 모드인 경우 다음 코드를 사용하여 노출 기간 및 ISO를 조정할 수 있습니다.

CaptureDevice.LockForConfiguration(out Error);
CaptureDevice.LockExposure(DurationValue,ISOValue,null);
CaptureDevice.UnlockForConfiguration();

자동 및 잠금 모드에서 애플리케이션은 다음 코드를 사용하여 자동 노출 루틴의 바이어스를 조정할 수 있습니다.

CaptureDevice.LockForConfiguration(out Error);
CaptureDevice.SetExposureTargetBias(Value,null);
CaptureDevice.UnlockForConfiguration();

최소 및 최대 설정 범위는 애플리케이션이 실행 중인 디바이스에 따라 달라지므로 하드 코딩해서는 안 됩니다. 대신 다음 속성을 사용하여 최소 및 최대 값 범위를 가져옵니다.

  • CaptureDevice.MinExposureTargetBias
  • CaptureDevice.MaxExposureTargetBias
  • CaptureDevice.ActiveFormat.MinISO
  • CaptureDevice.ActiveFormat.MaxISO
  • CaptureDevice.ActiveFormat.MinExposureDuration
  • CaptureDevice.ActiveFormat.MaxExposureDuration

위의 코드에서 볼 수 있듯이 캡처 디바이스는 구성을 위해 잠겨 있어야만 노출이 변경될 수 있습니다.

수동 노출 예제

일반 AV 캡처 설정 코드가 준비 UIViewController 되면 애플리케이션의 Storyboard에 추가하고 다음과 같이 구성할 수 있습니다.

UIViewController를 애플리케이션 Storyboard에 추가하고 수동 노출 예제에 대해 여기에 표시된 대로 구성할 수 있습니다.

뷰에는 다음과 같은 주요 요소가 포함됩니다.

  • 비디오 피드를 표시하는 A UIImageView 입니다.
  • UISegmentedControl 포커스 모드를 자동에서 잠김으로 변경하는 A입니다.
  • 오프셋, 기간, ISO 및 바이어스를 표시하고 업데이트하는 4개의 UISlider 컨트롤입니다.

수동 노출 제어를 위해 뷰 컨트롤러를 연결하려면 다음을 수행합니다.

  1. 다음 using 명령문을 추가합니다.

    using System;
    using Foundation;
    using UIKit;
    using System.CodeDom.Compiler;
    using System.Collections.Generic;
    using System.Linq;
    using AVFoundation;
    using CoreVideo;
    using CoreMedia;
    using CoreGraphics;
    using CoreFoundation;
    using System.Timers;
    
  2. 다음 프라이빗 변수를 추가합니다.

    #region Private Variables
    private NSError Error;
    private bool Automatic = true;
    private nfloat ExposureDurationPower = 5;
    private nfloat ExposureMinimumDuration = 1.0f/1000.0f;
    #endregion
    
  3. 다음 계산 속성을 추가합니다.

    #region Computed Properties
    public AppDelegate ThisApp {
        get { return (AppDelegate)UIApplication.SharedApplication.Delegate; }
    }
    public Timer SampleTimer { get; set; }
    #endregion
    
  4. 메서드를 재정의 ViewDidLoad 하고 다음 코드를 추가합니다.

    public override void ViewDidLoad ()
    {
        base.ViewDidLoad ();
    
        // Hide no camera label
        NoCamera.Hidden = ThisApp.CameraAvailable;
    
        // Attach to camera view
        ThisApp.Recorder.DisplayView = CameraView;
    
        // Set min and max values
        Offset.MinValue = ThisApp.CaptureDevice.MinExposureTargetBias;
        Offset.MaxValue = ThisApp.CaptureDevice.MaxExposureTargetBias;
    
        Duration.MinValue = 0.0f;
        Duration.MaxValue = 1.0f;
    
        ISO.MinValue = ThisApp.CaptureDevice.ActiveFormat.MinISO;
        ISO.MaxValue = ThisApp.CaptureDevice.ActiveFormat.MaxISO;
    
        Bias.MinValue = ThisApp.CaptureDevice.MinExposureTargetBias;
        Bias.MaxValue = ThisApp.CaptureDevice.MaxExposureTargetBias;
    
        // Create a timer to monitor and update the UI
        SampleTimer = new Timer (5000);
        SampleTimer.Elapsed += (sender, e) => {
            // Update position slider
            Offset.BeginInvokeOnMainThread(() =>{
                Offset.Value = ThisApp.Input.Device.ExposureTargetOffset;
            });
    
            Duration.BeginInvokeOnMainThread(() =>{
                var newDurationSeconds = CMTimeGetSeconds(ThisApp.Input.Device.ExposureDuration);
                var minDurationSeconds = Math.Max(CMTimeGetSeconds(ThisApp.CaptureDevice.ActiveFormat.MinExposureDuration), ExposureMinimumDuration);
                var maxDurationSeconds = CMTimeGetSeconds(ThisApp.CaptureDevice.ActiveFormat.MaxExposureDuration);
                var p = (newDurationSeconds - minDurationSeconds) / (maxDurationSeconds - minDurationSeconds);
                Duration.Value = (float)Math.Pow(p, 1.0f/ExposureDurationPower);
            });
    
            ISO.BeginInvokeOnMainThread(() => {
                ISO.Value = ThisApp.Input.Device.ISO;
            });
    
            Bias.BeginInvokeOnMainThread(() => {
                Bias.Value = ThisApp.Input.Device.ExposureTargetBias;
            });
        };
    
        // Watch for value changes
        Segments.ValueChanged += (object sender, EventArgs e) => {
    
            // Lock device for change
            ThisApp.CaptureDevice.LockForConfiguration(out Error);
    
            // Take action based on the segment selected
            switch(Segments.SelectedSegment) {
            case 0:
                // Activate auto exposure and start monitoring position
                Duration.Enabled = false;
                ISO.Enabled = false;
                ThisApp.CaptureDevice.ExposureMode = AVCaptureExposureMode.ContinuousAutoExposure;
                SampleTimer.Start();
                Automatic = true;
                break;
            case 1:
                // Lock exposure and allow the user to control the camera
                SampleTimer.Stop();
                ThisApp.CaptureDevice.ExposureMode = AVCaptureExposureMode.Locked;
                Automatic = false;
                Duration.Enabled = false;
                ISO.Enabled = false;
                break;
            case 2:
                // Custom exposure and allow the user to control the camera
                SampleTimer.Stop();
                ThisApp.CaptureDevice.ExposureMode = AVCaptureExposureMode.Custom;
                Automatic = false;
                Duration.Enabled = true;
                ISO.Enabled = true;
                break;
            }
    
            // Unlock device
            ThisApp.CaptureDevice.UnlockForConfiguration();
        };
    
        // Monitor position changes
        Duration.ValueChanged += (object sender, EventArgs e) => {
    
            // If we are in the automatic mode, ignore changes
            if (Automatic) return;
    
            // Calculate value
            var p = Math.Pow(Duration.Value,ExposureDurationPower);
            var minDurationSeconds = Math.Max(CMTimeGetSeconds(ThisApp.CaptureDevice.ActiveFormat.MinExposureDuration),ExposureMinimumDuration);
            var maxDurationSeconds = CMTimeGetSeconds(ThisApp.CaptureDevice.ActiveFormat.MaxExposureDuration);
            var newDurationSeconds = p * (maxDurationSeconds - minDurationSeconds) +minDurationSeconds;
    
            // Update Focus position
            ThisApp.CaptureDevice.LockForConfiguration(out Error);
            ThisApp.CaptureDevice.LockExposure(CMTime.FromSeconds(p,1000*1000*1000),ThisApp.CaptureDevice.ISO,null);
            ThisApp.CaptureDevice.UnlockForConfiguration();
        };
    
        ISO.ValueChanged += (object sender, EventArgs e) => {
    
            // If we are in the automatic mode, ignore changes
            if (Automatic) return;
    
            // Update Focus position
            ThisApp.CaptureDevice.LockForConfiguration(out Error);
            ThisApp.CaptureDevice.LockExposure(ThisApp.CaptureDevice.ExposureDuration,ISO.Value,null);
            ThisApp.CaptureDevice.UnlockForConfiguration();
        };
    
        Bias.ValueChanged += (object sender, EventArgs e) => {
    
            // If we are in the automatic mode, ignore changes
            // if (Automatic) return;
    
            // Update Focus position
            ThisApp.CaptureDevice.LockForConfiguration(out Error);
            ThisApp.CaptureDevice.SetExposureTargetBias(Bias.Value,null);
            ThisApp.CaptureDevice.UnlockForConfiguration();
        };
    }
    
  5. 메서드를 재정의 ViewDidAppear 하고 다음을 추가하여 보기가 로드되면 기록을 시작합니다.

    public override void ViewDidAppear (bool animated)
    {
        base.ViewDidAppear (animated);
    
        // Start udating the display
        if (ThisApp.CameraAvailable) {
            // Remap to this camera view
            ThisApp.Recorder.DisplayView = CameraView;
    
            ThisApp.Session.StartRunning ();
            SampleTimer.Start ();
        }
    }
    
  6. 자동 모드에서 카메라를 사용하면 카메라가 노출을 조정하면 슬라이더가 자동으로 이동합니다.

    카메라가 노출을 조정하면 슬라이더가 자동으로 이동합니다.

  7. 잠긴 세그먼트를 탭하고 바이어스 슬라이더를 끌어 자동 노출의 바이어스 부분을 수동으로 조정합니다.

    자동 노출의 바이어스 수동 조정

  8. 사용자 지정 세그먼트를 탭하고 Duration 및 ISO 슬라이더를 끌어 노출을 수동으로 제어합니다.

    기간 및 ISO 슬라이더를 끌어 노출을 수동으로 제어합니다.

  9. 애플리케이션을 중지합니다.

위의 코드에서는 카메라가 자동 모드에 있을 때 노출 설정을 모니터링하는 방법과 슬라이더를 사용하여 잠금 모드 또는 사용자 지정 모드에 있을 때 노출을 제어하는 방법을 보여 줍니다.

수동 화이트 밸런스

화이트 밸런스 컨트롤을 사용하면 이미지에서 거신의 균형을 조정하여 보다 사실적으로 보이게 할 수 있습니다. 광원의 색 온도가 다르며 이미지를 캡처하는 데 사용되는 카메라 설정을 조정하여 이러한 차이를 보정해야 합니다. 다시 말하지만, 화이트 밸런스에 대한 사용자 제어를 허용함으로써 자동 루틴이 예술적 효과를 달성할 수 없는 전문적인 조정을 할 수 있습니다.

수동 화이트 밸런스 조정을 보여 주는 샘플 이미지

예를 들어 일광에는 푸른 색의 캐스트가 있는 반면, 텅스텐 백열등에는 따뜻한 노란색-주황색 색조가 있습니다. (혼란스럽게도 "쿨" 색은 "따뜻한" 색보다 색 온도가 높습니다. 색 온도는 지각이 아닌 물리적 측정값입니다.)

인간의 마음은 색상 온도의 차이에 대한 보상에 매우 능숙하지만, 이것은 카메라가 할 수없는 일입니다. 카메라는 반대 스펙트럼에서 색을 증폭시켜 색상 차이를 조정합니다.

새로운 iOS 8 노출 API를 사용하면 애플리케이션이 프로세스를 제어하고 카메라의 화이트 밸런스 설정을 세밀하게 제어할 수 있습니다.

화이트 밸런스 작동 방식

IOS 8 애플리케이션에서 화이트 밸런스를 제어하는 세부 정보를 논의하기 전에. 화이트 밸런스의 작동 방식을 간단히 살펴보겠습니다.

색 인식 연구에서 CIE 1931 RGB 색 공간과 CIE 1931 XYZ 색 공간 은 수학적으로 정의된 첫 번째 색 공간입니다. 그들은 1931 년에 국제 일루미네이션위원회 (CIE)에 의해 만들어졌습니다.

CIE 1931 RGB 색 공간 및 CIE 1931 XYZ 색 공간

위의 차트는 진한 파란색에서 밝은 녹색, 밝은 빨간색에 이르기까지 인간의 눈에 보이는 모든 색을 보여줍니다. 위의 그래프에 표시된 것처럼 다이어그램의 모든 점을 X 및 Y 값으로 표시할 수 있습니다.

그래프에 보이는 것처럼 그래프에 그려질 수 있는 X 및 Y 값이 있는데, 이 값은 인간의 시야 범위를 벗어나기 때문에 카메라에서 이러한 색을 재현할 수 없습니다.

위의 차트에서 더 작은 곡선을 플랑키안 로커스(Planckian Locus)라고 하며, 색 온도(켈빈도)를 나타내며, 파란색 면(더 뜨거름)에 더 높은 숫자와 빨간색 면(쿨러)의 낮은 숫자로 표현됩니다. 이는 일반적인 조명 상황에 유용합니다.

혼합 조명 조건에서 화이트 밸런스 조정은 필요한 변경을 위해 플랑키안 궤적에서 벗어나야 합니다. 이러한 경우 조정을 CIE 눈금의 녹색 또는 빨간색/자홍색으로 이동해야 합니다.

iOS 디바이스는 반대 색 게인을 향상시켜 색 캐스트를 보정합니다. 예를 들어 장면에 파란색이 너무 많으면 빨간색 게인을 보정하기 위해 승격됩니다. 이러한 게인 값은 디바이스에 따라 달라지도록 특정 디바이스에 대해 보정됩니다.

기존 White Balance 컨트롤

iOS 7 이상에서는 속성을 통해 WhiteBalanceMode 다음과 같은 기존 White Balance 컨트롤을 제공했습니다.

  • AVCapture WhiteBalance ModeLocked – 장면을 한 번 샘플링하고 장면 전체에서 해당 값을 사용합니다.
  • AVCapture WhiteBalance ModeContinuousAutoExposure – 장면을 지속적으로 샘플링하여 균형이 잘 조정되었는지 확인합니다.

또한 애플리케이션은 속성을 모니터링 AdjustingWhiteBalance 하여 노출이 조정되는 시기를 확인할 수 있습니다.

iOS 8의 새 화이트 밸런스 컨트롤

iOS 7 이상에서 이미 제공하는 기능 외에도 이제 iOS 8에서 White Balance를 제어할 수 있는 다음 기능을 사용할 수 있습니다.

  • 디바이스 RGB의 완전 수동 제어가 향상됩니다.
  • 가져오기, 설정 및 키-값은 디바이스 RGB 향상을 관찰합니다.
  • 회색 카드를 사용한 화이트 밸런스 지원.
  • 디바이스 독립적 색 공간 간 변환 루틴입니다.

위의 기능을 구현하기 위해 구조체가 AVCaptureWhiteBalanceGain 다음 멤버와 함께 추가되었습니다.

  • RedGain
  • GreenGain
  • BlueGain

최대 화이트 밸런스 게인은 현재 4(4)이며 속성에서 MaxWhiteBalanceGain 준비할 수 있습니다. 따라서 법적 범위는 현재 1에서 (4)까지 MaxWhiteBalanceGain 입니다.

DeviceWhiteBalanceGains 속성을 사용하여 현재 값을 관찰할 수 있습니다. 카메라가 잠긴 흰색 균형 모드에 있을 때 잔액 증가를 조정하는 데 사용합니다 SetWhiteBalanceModeLockedWithDeviceWhiteBalanceGains .

변환 루틴

디바이스 독립적 색 공간으로 변환하는 데 도움이 되도록 iOS 8에 변환 루틴이 추가되었습니다. 변환 루틴을 구현하기 위해 구조체 AVCaptureWhiteBalanceChromaticityValues 가 다음 멤버와 함께 추가되었습니다.

  • X - 0에서 1까지의 값입니다.
  • Y - 0에서 1까지의 값입니다.

AVCaptureWhiteBalanceTemperatureAndTintValues 다음 멤버와 함께 구조체도 추가되었습니다.

  • Temperature - 켈빈도의 부동 소수점 값입니다.
  • Tint - 녹색 또는 마젠타에서 0에서 150까지의 오프셋이며, 양수 값은 녹색 방향으로, 음수는 마젠타 방향으로 향합니다.

CaptureDevice.GetTemperatureAndTintValues온도와 CaptureDevice.GetDeviceWhiteBalanceGains색조, 색채 및 RGB 게인 색 공간 간에 변환하려면 이 메서드와 메서드를 사용합니다.

참고 항목

변환 루틴은 변환할 값이 플랑키안 로커스에 가까울수록 더 정확합니다.

회색 카드 지원

Apple은 회색 세계라는 용어를 사용하여 iOS 8에 기본 제공되는 회색 카드 지원을 참조합니다. 이를 통해 사용자는 프레임 중앙의 50% 이상을 커버하고 이를 사용하여 화이트 밸런스를 조정하는 실제 회색 카드에 집중할 수 있습니다. 회색 카드의 목적은 중립으로 보이는 흰색을 달성하는 것입니다.

이는 사용자에게 카메라 앞에 물리적 회색 카드를 배치하라는 메시지를 표시하고, 속성을 모니터링하고, 값이 정착될 때까지 대기하여 GrayWorldDeviceWhiteBalanceGains 애플리케이션에서 구현할 수 있습니다.

그러면 애플리케이션은 속성의 값을 GrayWorldDeviceWhiteBalanceGains 사용하여 메서드에 SetWhiteBalanceModeLockedWithDeviceWhiteBalanceGains 대한 White Balance 이득을 잠그고 변경 내용을 적용합니다.

White Balance를 변경하려면 먼저 구성을 위해 캡처 디바이스를 잠가야 합니다.

수동 화이트 밸런스 예제

일반 AV 캡처 설정 코드가 준비 UIViewController 되면 애플리케이션의 Storyboard에 추가하고 다음과 같이 구성할 수 있습니다.

UIViewController를 애플리케이션 Storyboard에 추가하고 수동 화이트 밸런스 예제에 대해 여기에 표시된 대로 구성할 수 있습니다.

뷰에는 다음과 같은 주요 요소가 포함됩니다.

  • 비디오 피드를 표시하는 A UIImageView 입니다.
  • UISegmentedControl 포커스 모드를 자동에서 잠김으로 변경하는 A입니다.
  • 온도 및 색조를 표시하고 업데이트하는 두 개의 UISlider 컨트롤입니다.
  • UIButton 회색 카드(회색 세계) 공간을 샘플링하고 해당 값을 사용하여 화이트 밸런스를 설정하는 데 사용됩니다.

수동 화이트 밸런스 컨트롤에 대한 뷰 컨트롤러를 연결하려면 다음을 수행합니다.

  1. 다음 using 명령문을 추가합니다.

    using System;
    using Foundation;
    using UIKit;
    using System.CodeDom.Compiler;
    using System.Collections.Generic;
    using System.Linq;
    using AVFoundation;
    using CoreVideo;
    using CoreMedia;
    using CoreGraphics;
    using CoreFoundation;
    using System.Timers;
    
  2. 다음 프라이빗 변수를 추가합니다.

    #region Private Variables
    private NSError Error;
    private bool Automatic = true;
    #endregion
    
  3. 다음 계산 속성을 추가합니다.

    #region Computed Properties
    public AppDelegate ThisApp {
        get { return (AppDelegate)UIApplication.SharedApplication.Delegate; }
    }
    public Timer SampleTimer { get; set; }
    #endregion
    
  4. 다음 프라이빗 메서드를 추가하여 새 화이트 밸런스 온도 및 색조를 설정합니다.

    #region Private Methods
    void SetTemperatureAndTint() {
        // Grab current temp and tint
        var TempAndTint = new AVCaptureWhiteBalanceTemperatureAndTintValues (Temperature.Value, Tint.Value);
    
        // Convert Color space
        var gains = ThisApp.CaptureDevice.GetDeviceWhiteBalanceGains (TempAndTint);
    
        // Set the new values
        if (ThisApp.CaptureDevice.LockForConfiguration (out Error)) {
            gains = NomralizeGains (gains);
            ThisApp.CaptureDevice.SetWhiteBalanceModeLockedWithDeviceWhiteBalanceGains (gains, null);
            ThisApp.CaptureDevice.UnlockForConfiguration ();
        }
    }
    
    AVCaptureWhiteBalanceGains NomralizeGains (AVCaptureWhiteBalanceGains gains)
    {
        gains.RedGain = Math.Max (1, gains.RedGain);
        gains.BlueGain = Math.Max (1, gains.BlueGain);
        gains.GreenGain = Math.Max (1, gains.GreenGain);
    
        float maxGain = ThisApp.CaptureDevice.MaxWhiteBalanceGain;
        gains.RedGain = Math.Min (maxGain, gains.RedGain);
        gains.BlueGain = Math.Min (maxGain, gains.BlueGain);
        gains.GreenGain = Math.Min (maxGain, gains.GreenGain);
    
        return gains;
    }
    #endregion
    
  5. 메서드를 재정의 ViewDidLoad 하고 다음 코드를 추가합니다.

    public override void ViewDidLoad ()
    {
        base.ViewDidLoad ();
    
        // Hide no camera label
        NoCamera.Hidden = ThisApp.CameraAvailable;
    
        // Attach to camera view
        ThisApp.Recorder.DisplayView = CameraView;
    
        // Set min and max values
        Temperature.MinValue = 1000f;
        Temperature.MaxValue = 10000f;
    
        Tint.MinValue = -150f;
        Tint.MaxValue = 150f;
    
        // Create a timer to monitor and update the UI
        SampleTimer = new Timer (5000);
        SampleTimer.Elapsed += (sender, e) => {
            // Convert color space
            var TempAndTint = ThisApp.CaptureDevice.GetTemperatureAndTintValues (ThisApp.CaptureDevice.DeviceWhiteBalanceGains);
    
            // Update slider positions
            Temperature.BeginInvokeOnMainThread (() => {
                Temperature.Value = TempAndTint.Temperature;
            });
    
            Tint.BeginInvokeOnMainThread (() => {
                Tint.Value = TempAndTint.Tint;
            });
        };
    
        // Watch for value changes
        Segments.ValueChanged += (sender, e) => {
            // Lock device for change
            if (ThisApp.CaptureDevice.LockForConfiguration (out Error)) {
    
                // Take action based on the segment selected
                switch (Segments.SelectedSegment) {
                case 0:
                // Activate auto focus and start monitoring position
                    Temperature.Enabled = false;
                    Tint.Enabled = false;
                    ThisApp.CaptureDevice.WhiteBalanceMode = AVCaptureWhiteBalanceMode.ContinuousAutoWhiteBalance;
                    SampleTimer.Start ();
                    Automatic = true;
                    break;
                case 1:
                // Stop auto focus and allow the user to control the camera
                    SampleTimer.Stop ();
                    ThisApp.CaptureDevice.WhiteBalanceMode = AVCaptureWhiteBalanceMode.Locked;
                    Automatic = false;
                    Temperature.Enabled = true;
                    Tint.Enabled = true;
                    break;
                }
    
                // Unlock device
                ThisApp.CaptureDevice.UnlockForConfiguration ();
            }
        };
    
        // Monitor position changes
        Temperature.TouchUpInside += (sender, e) => {
    
            // If we are in the automatic mode, ignore changes
            if (Automatic)
                return;
    
            // Update white balance
            SetTemperatureAndTint ();
        };
    
        Tint.TouchUpInside += (sender, e) => {
    
            // If we are in the automatic mode, ignore changes
            if (Automatic)
                return;
    
            // Update white balance
            SetTemperatureAndTint ();
        };
    
        GrayCardButton.TouchUpInside += (sender, e) => {
    
            // If we are in the automatic mode, ignore changes
            if (Automatic)
                return;
    
            // Get gray card values
            var gains = ThisApp.CaptureDevice.GrayWorldDeviceWhiteBalanceGains;
    
            // Set the new values
            if (ThisApp.CaptureDevice.LockForConfiguration (out Error)) {
                ThisApp.CaptureDevice.SetWhiteBalanceModeLockedWithDeviceWhiteBalanceGains (gains, null);
                ThisApp.CaptureDevice.UnlockForConfiguration ();
            }
        };
    }
    
  6. 메서드를 재정의 ViewDidAppear 하고 다음을 추가하여 보기가 로드되면 기록을 시작합니다.

    public override void ViewDidAppear (bool animated)
    {
        base.ViewDidAppear (animated);
    
        // Start udating the display
        if (ThisApp.CameraAvailable) {
            // Remap to this camera view
            ThisApp.Recorder.DisplayView = CameraView;
    
            ThisApp.Session.StartRunning ();
            SampleTimer.Start ();
        }
    }
    
  7. 변경 내용을 코드에 저장하고 애플리케이션을 실행합니다.

  8. 자동 모드에서 카메라를 사용하면 카메라가 흰색 균형을 조정하면 슬라이더가 자동으로 이동합니다.

    카메라가 흰색 균형을 조정하면 슬라이더가 자동으로 이동합니다.

  9. 잠긴 세그먼트를 탭하고 Temp 및 Tint 슬라이더를 끌어 흰색 균형을 수동으로 조정합니다.

    임시 및 색조 슬라이더를 끌어 흰색 균형을 수동으로 조정합니다.

  10. 잠긴 세그먼트를 선택한 상태에서 카메라 앞에 실제 회색 카드를 놓고 회색 카드 단추를 탭하여 회색 세계에 흰색 균형을 조정합니다.

    회색 카드 단추를 탭하여 회색 세계에 흰색 균형 조정

  11. 애플리케이션을 중지합니다.

위의 코드에서는 카메라가 자동 모드일 때 흰색 균형 설정을 모니터링하거나 슬라이더를 사용하여 잠금 모드에 있을 때 흰색 균형을 제어하는 방법을 보여 줍니다.

대괄호로 묶인 캡처

대괄호로 묶은 캡처는 위에 표시된 수동 카메라 컨트롤의 설정을 기반으로 하며 애플리케이션이 다양한 방법으로 시간을 캡처할 수 있도록 합니다.

간단히 말해서, 대괄호 캡처는 그림에서 사진까지 다양한 설정으로 촬영한 스틸 이미지의 버스트입니다.

대괄호 캡처 작동 방식

애플리케이션은 iOS 8에서 대괄호로 묶은 캡처를 사용하여 일련의 수동 카메라 컨트롤을 미리 설정하여 단일 명령을 실행하고 현재 장면에서 각 수동 사전 설정에 대해 일련의 이미지를 반환하게 할 수 있습니다.

대괄호로 묶인 캡처 기본 사항

다시 말하지만, 대괄호로 묶인 캡처는 그림에서 사진까지 다양한 설정으로 촬영한 스틸 이미지의 버스트입니다. 사용할 수 있는 대괄호 캡처 유형은 다음과 같습니다.

  • 자동 노출 대괄호 - 모든 이미지의 바이어스 크기가 다양합니다.
  • 수동 노출 브래킷 - 모든 이미지의 셔터 속도(기간) 및 ISO 크기가 다양합니다.
  • 단순 버스트 브래킷 – 연속해서 연속해서 촬영한 일련의 스틸 이미지입니다.

iOS 8의 새 대괄호 캡처 컨트롤

모든 대괄호 캡처 명령은 클래스에서 AVCaptureStillImageOutput 구현됩니다. 이 메서드를 CaptureStillImageBracket사용하여 지정된 설정 배열로 일련의 이미지를 가져옵니다.

설정을 처리하기 위해 두 개의 새 클래스가 구현되었습니다.

  • AVCaptureAutoExposureBracketedStillImageSettings – 자동 노출 대괄호에 대한 바이어스 설정에 사용되는 하나의 속성 ExposureTargetBias이 있습니다.
  • AVCaptureManual ExposureBracketedStillImageSettings– 두 가지 속성이 있으며ISO, ExposureDuration 수동 노출 브래킷에 대한 셔터 속도와 ISO를 설정하는 데 사용됩니다.

대괄호로 묶인 캡처 컨트롤 할 일 및 하지 않음

해야 할 일

다음은 iOS 8에서 대괄호 캡처 컨트롤을 사용할 때 수행해야 하는 작업 목록입니다.

  • 메서드를 호출하여 최악의 경우 캡처 상황에 대비하여 앱을 준비합니다 PrepareToCaptureStillImageBracket .
  • 샘플 버퍼가 동일한 공유 풀에서 온다고 가정합니다.
  • 이전 준비 호출에 의해 할당된 메모리를 해제하려면 다시 호출 PrepareToCaptureStillImageBracket 하고 한 개체의 배열을 보냅니다.

금지 사항

다음은 iOS 8에서 대괄호 캡처 컨트롤을 사용할 때 수행해서는 안 되는 작업 목록입니다.

  • 단일 캡처에서 대괄호로 묶인 캡처 설정 유형을 혼합하지 마세요.
  • 단일 캡처에서 이미지 이상을 MaxBracketedCaptureStillImageCount 요청하지 마세요.

대괄호로 묶인 캡처 세부 정보

iOS 8에서 대괄호로 묶은 캡처를 사용할 때는 다음 세부 정보를 고려해야 합니다.

  • 대괄호로 묶인 설정은 일시적으로 설정을 재정의합니다 AVCaptureDevice .
  • 플래시 및 스틸 이미지 손떨림 보정 설정은 무시됩니다.
  • 모든 이미지는 동일한 출력 형식(jpeg, png 등)을 사용해야 합니다.
  • 비디오 미리 보기는 프레임을 삭제할 수 있습니다.
  • 대괄호로 묶은 캡처는 iOS 8과 호환되는 모든 디바이스에서 지원됩니다.

이 정보를 염두에 두고 iOS 8에서 대괄호로 묶은 캡처를 사용하는 예제를 살펴보겠습니다.

대괄호 캡처 예제

일반 AV 캡처 설정 코드가 준비 UIViewController 되면 애플리케이션의 Storyboard에 추가하고 다음과 같이 구성할 수 있습니다.

UIViewController를 애플리케이션 Storyboard에 추가하고 여기에 표시된 대로 대괄호 캡처 예제로 구성할 수 있습니다.

뷰에는 다음과 같은 주요 요소가 포함됩니다.

  • 비디오 피드를 표시하는 A UIImageView 입니다.
  • 캡처 결과를 표시하는 세 UIImageViews 가지입니다.
  • 비디오 UIScrollView 피드 및 결과 보기를 보관하는 A입니다.
  • UIButton 일부 미리 설정된 설정으로 대괄호로 묶인 캡처를 사용하는 데 사용됩니다.

다음을 수행하여 대괄호로 묶인 캡처용 보기 컨트롤러를 연결합니다.

  1. 다음 using 명령문을 추가합니다.

    using System;
    using System.Drawing;
    using Foundation;
    using UIKit;
    using System.CodeDom.Compiler;
    using System.Collections.Generic;
    using System.Linq;
    using AVFoundation;
    using CoreVideo;
    using CoreMedia;
    using CoreGraphics;
    using CoreFoundation;
    using CoreImage;
    
  2. 다음 프라이빗 변수를 추가합니다.

    #region Private Variables
    private NSError Error;
    private List<UIImageView> Output = new List<UIImageView>();
    private nint OutputIndex = 0;
    #endregion
    
  3. 다음 계산 속성을 추가합니다.

    #region Computed Properties
    public AppDelegate ThisApp {
        get { return (AppDelegate)UIApplication.SharedApplication.Delegate; }
    }
    #endregion
    
  4. 다음 프라이빗 메서드를 추가하여 필요한 출력 이미지 뷰를 빌드합니다.

    #region Private Methods
    private UIImageView BuildOutputView(nint n) {
    
        // Create a new image view controller
        var imageView = new UIImageView (new CGRect (CameraView.Frame.Width * n, 0, CameraView.Frame.Width, CameraView.Frame.Height));
    
        // Load a temp image
        imageView.Image = UIImage.FromFile ("Default-568h@2x.png");
    
        // Add a label
        UILabel label = new UILabel (new CGRect (0, 20, CameraView.Frame.Width, 24));
        label.TextColor = UIColor.White;
        label.Text = string.Format ("Bracketed Image {0}", n);
        imageView.AddSubview (label);
    
        // Add to scrolling view
        ScrollView.AddSubview (imageView);
    
        // Return new image view
        return imageView;
    }
    #endregion
    
  5. 메서드를 재정의 ViewDidLoad 하고 다음 코드를 추가합니다.

    public override void ViewDidLoad ()
    {
        base.ViewDidLoad ();
    
        // Hide no camera label
        NoCamera.Hidden = ThisApp.CameraAvailable;
    
        // Attach to camera view
        ThisApp.Recorder.DisplayView = CameraView;
    
        // Setup scrolling area
        ScrollView.ContentSize = new SizeF (CameraView.Frame.Width * 4, CameraView.Frame.Height);
    
        // Add output views
        Output.Add (BuildOutputView (1));
        Output.Add (BuildOutputView (2));
        Output.Add (BuildOutputView (3));
    
        // Create preset settings
        var Settings = new AVCaptureBracketedStillImageSettings[] {
            AVCaptureAutoExposureBracketedStillImageSettings.Create(-2.0f),
            AVCaptureAutoExposureBracketedStillImageSettings.Create(0.0f),
            AVCaptureAutoExposureBracketedStillImageSettings.Create(2.0f)
        };
    
        // Wireup capture button
        CaptureButton.TouchUpInside += (sender, e) => {
            // Reset output index
            OutputIndex = 0;
    
            // Tell the camera that we are getting ready to do a bracketed capture
            ThisApp.StillImageOutput.PrepareToCaptureStillImageBracket(ThisApp.StillImageOutput.Connections[0],Settings,async (bool ready, NSError err) => {
                // Was there an error, if so report it
                if (err!=null) {
                    Console.WriteLine("Error: {0}",err.LocalizedDescription);
                }
            });
    
            // Ask the camera to snap a bracketed capture
            ThisApp.StillImageOutput.CaptureStillImageBracket(ThisApp.StillImageOutput.Connections[0],Settings, (sampleBuffer, settings, err) =>{
                // Convert raw image stream into a Core Image Image
                var imageData = AVCaptureStillImageOutput.JpegStillToNSData(sampleBuffer);
                var image = CIImage.FromData(imageData);
    
                // Display the resulting image
                Output[OutputIndex++].Image = UIImage.FromImage(image);
    
                // IMPORTANT: You must release the buffer because AVFoundation has a fixed number
                // of buffers and will stop delivering frames if it runs out.
                sampleBuffer.Dispose();
            });
        };
    }
    
  6. 메서드를 재정의 ViewDidAppear 하고 다음 코드를 추가합니다.

    public override void ViewDidAppear (bool animated)
    {
        base.ViewDidAppear (animated);
    
        // Start udating the display
        if (ThisApp.CameraAvailable) {
            // Remap to this camera view
            ThisApp.Recorder.DisplayView = CameraView;
    
            ThisApp.Session.StartRunning ();
        }
    }
    
    
  7. 변경 내용을 코드에 저장하고 애플리케이션을 실행합니다.

  8. 장면을 프레임하고 대괄호 캡처 단추를 탭합니다.

    장면 프레임을 만들고 대괄호 캡처 단추를 탭합니다.

  9. 오른쪽으로 살짝 밀어 대괄호 캡처에서 찍은 세 개의 이미지를 확인합니다.

    오른쪽으로 살짝 밀어 대괄호 캡처에서 찍은 세 개의 이미지를 확인합니다.

  10. 애플리케이션을 중지합니다.

위의 코드에서는 iOS 8에서 자동 노출 대괄호 캡처를 구성하고 사용하는 방법을 보여 했습니다.

요약

이 문서에서는 iOS 8에서 제공하는 새로운 수동 카메라 컨트롤에 대한 소개와 이러한 컨트롤이 수행하는 작업 및 작동 방식의 기본 사항을 설명했습니다. 수동 포커스, 수동 노출 및 수동 화이트 밸런스의 예를 제공했습니다. 마지막으로 앞에서 설명한 수동 카메라 컨트롤을 사용하여 대괄호로 묶은 캡처를 사용하는 예제를 제공했습니다.