Share via


Xamarin.iOS の手動カメラ コントロール

iOS 8 の AVFoundation Framework で提供される手動カメラ コントロールを使用すると、モバイル アプリケーションで iOS デバイスのカメラを完全に制御できます。 このきめ細かいレベルのコントロールを使用して、専門的レベルのカメラ アプリケーションを作成し、静止画像やビデオを撮影しながらカメラのパラメータを調整することで、芸術的な創作を提供できます。

これらのコントロールは、画像の正確性や美しさを問う結果をあまり求めておらず、撮影される画像の特徴や要素を強調する用途が多い科学的または産業的なアプリケーションを開発する場合にも役立ちます。

AVFoundation Capture オブジェクト

iOS デバイスでカメラを使用して撮影するのがビデオか静止画像かにかかわらず、それらの画像のキャプチャに使用されるプロセスはほとんど同じです。 これは、既定の自動カメラ コントロールを使用するアプリケーション、または新しい手動カメラ コントロールを利用するアプリケーションに当てはまります。

AVFoundation Capture オブジェクトの概要

入力は、AVCaptureDeviceInput から AVCaptureConnection を使用して AVCaptureSession に取得されます。 結果は、静止画像またはビデオ ストリームとして出力されます。 プロセス全体が、AVCaptureDevice によって制御されます。

提供される手動コントロール

iOS 8 で提供される新しい API を使用して、アプリケーションは次のカメラ機能を制御できます。

  • 手動フォーカス - エンド ユーザーがフォーカスを直接制御できるようにすることで、アプリケーションは撮影した画像のより詳細な制御を提供できます。
  • 手動露出 - 露出の手動コントロールを提供することで、アプリケーションはユーザーの自由度を高め、定型化された外観を実現できるようにします。
  • 手動ホワイト バランス - ホワイト バランスは、画像の色を調整するために使用されます。多くの場合、リアルに見えるようにするのがその目的です。 光源によって色温度が異なり、画像のキャプチャに使用されるカメラ設定は、これらの違いを補正するために調整されます。 ここでも、ユーザーがホワイト バランスを制御できるようにすることで、ユーザーは自動的には実行されない調整が行えます。

iOS 8 では、イメージ キャプチャ プロセスをきめ細かく制御できるように、既存の iOS API の拡張機能と機能強化が提供されます。

要件

この記事で説明する手順を完了するには、次のものが必要です:

  • Xcode 7 以降および iOS 8 以降 - Apple の Xcode 7 および iOS 8 以降の API を開発者のコンピューターにインストールして構成する必要があります。
  • Visual Studio for Mac - 最新バージョンの Visual Studio for Mac をユーザー デバイスにインストールして構成する必要があります。
  • iOS 8 デバイス - 最新バージョンの iOS 8 を実行している iOS デバイス。 iOS シミュレーターではカメラ機能をテストできません。

一般的な AV キャプチャのセットアップ

iOS デバイスでビデオを録画する場合、常に必要ないくつかの一般的なセットアップ コードがあります。 このセクションでは、iOS デバイスのカメラからビデオを録画し、そのビデオを UIImageView でリアルタイムで表示するために必要な最小限のセットアップについて説明します。

出力サンプル バッファー デリゲート

最初に必要なものの 1 つは、サンプル出力バッファーを監視し、バッファーからアプリケーション UI の UIImageView に取り込まれたイメージを表示するためのデリゲートです。

次のルーチンは、サンプル バッファーを監視し、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 - フォーカスは 1 つのフォーカス ポイントでロックされます。
  • AVCaptureFocusModeAutoFocus - カメラは、フォーカスが鋭く集中する点を見つけるまで、すべての焦点を通ってレンズをスイープし、その後その点に留まります。
  • AVCaptureFocusModeContinuousAutoFocus - カメラがフォーカス外の状態を検出するたびに再フォーカスします。

既存のコントロールは、ユーザーがタップして特定の領域にフォーカスできるように、FocusPointOfInterest プロパティを介して設定可能な目的地も提供しました。 アプリケーションでは、IsAdjustingFocus プロパティを監視することで、レンズの動きを追跡することもできます。

さらに、AutoFocusRangeRestriction プロパティによって範囲の制限が次のように指定されました。

  • AVCaptureAutoFocusRangeRestrictionNear - オートフォーカスを近くの深度に制限します。 QR コードやバーコードのスキャンなどの状況で役立ちます。
  • AVCaptureAutoFocusRangeRestrictionFar - オートフォーカスを遠くの深度に制限します。 関係のないことがわかっているオブジェクトがビューの領域内 (ウィンドウ フレームなど) にある場合に便利です。

最後に、自動フォーカス アルゴリズムの速度を低下させ、ビデオの録画時に成果物の移動を回避するためにより小さな増分で処理する SmoothAutoFocus プロパティがあります。

iOS 8 の新しいフォーカス コントロール

iOS 7 以降で既に提供されている機能に加えて、次の機能を使用して iOS 8 でフォーカスを制御できるようになりました。

  • フォーカスをロックするときのレンズ位置を完全に手動で制御します。
  • フォーカス モードでのレンズ位置のキー値観察。

上記の機能を実装するために、AVCaptureDevice クラスは、カメラ レンズの現在位置を取得するために使用される読み取り専用の LensPosition プロパティを含むように変更されています。

レンズ位置を手動で制御するには、キャプチャ デバイスが Locked フォーカス モードである必要があります。 例:

CaptureDevice.FocusMode = AVCaptureFocusMode.Locked;

キャプチャ デバイスの SetFocusModeLocked メソッドは、カメラレンズの位置を調整するために使用されます。 オプションのコールバック ルーチンを提供して、変更が有効になったときに通知を受け取れるようにできます。 例:

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

上記のコードに示すように、レンズの位置を変更する前に、キャプチャ デバイスを構成用にロックする必要があります。 有効なレンズ位置の値は 0.0 から 1.0 です。

手動フォーカスの例

一般的な AV キャプチャのセットアップ コードを配置すると、UIViewController をアプリケーションのストーリーボードに追加し、次のように構成できます。

UIViewController をアプリケーションのストーリーボードに追加し、次に示すように手動フォーカスの例として構成できます。

このビューには、次の主要な要素が含まれています。

  • ビデオ フィードを表示する UIImageView
  • フォーカス モードを Automatic から Locked に変更する UISegmentedControl
  • 現在のレンズ位置を表示および更新する 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. カメラが Auto モードにすると、カメラがフォーカスを調整するにつれて、スライダーが自動的に移動します。

    このサンプル アプリでカメラがフォーカスを調整すると、スライダーが自動的に移動します

  7. [Locked] セグメントをタップし、位置スライダーをドラッグして、レンズの位置を手動で調整します。

    レンズの位置を手動で調整する

  8. アプリケーションを停止します。

上記のコードでは、カメラが Automatic モードのときにレンズの位置を監視する方法、または Locked モードのときにスライダーを使用してレンズの位置を制御する方法を示しています。

手動露出

露出とは、ソースの明るさに対する画像の明るさを指し、センサーに当たる光の量、長さ、センサーのゲイン レベル (ISO マッピング) によって決まります。 露出の手動コントロールを提供することで、アプリケーションはエンド ユーザーの自由度を高め、定型化された外観を実現できるようにします。

手動露出コントロールを使用すると、ユーザーは非現実的な明るさから雰囲気のある暗さまで幅広い明るさの画像を撮影できます。

非現実的な明るさから暗く憂鬱な様子までの露出を示す画像のサンプル

繰り返しになりますが、これは科学アプリケーション用のプログラムによる制御を使用して自動的に行うか、アプリケーションのユーザー インターフェイスによって提供される手動制御を使用して行えます。 いずれの場合も、新しい iOS 8 露出 API では、カメラの露出設定をきめ細かく制御できます。

露出のしくみ

IOS 8 アプリケーションでの露出の制御の詳細について説明する前に、 露出のしくみを簡単に見てみましょう。

露出のしくみ

露出を制御するためにまとめられている 3 つの基本的な要素は次のとおりです。

  • シャッター速度 - カメラ センサーに光を当てるためにシャッターが開いている時間の長さです。 シャッターを開く時間が短いほど、入る光が少なくなり、画像が鮮明になります (モーション ブラーが少なくなります)。 シャッターを開く時間が長いほど、入る光が多くなり、発生するモーション ブラーは多くなります。
  • ISO マッピング - これはフィルム写真から借用された用語であり、フィルム内の化学物質の光に対する感度を指します。 フィルムの ISO 値が低いと、粒度が低く、色の再現性は高くなり、デジタル センサーの ISO 値が低いと、センサー ノイズは少なくなるものの、明度は低くなります。 ISO 値が高いほど、画像は明るくなりますが、センサーのノイズが多くなります。 デジタル センサーの "ISO" は、物理的な特徴ではなく、電子ゲインの尺度です。
  • レンズ絞り - これはレンズ開口部のサイズです。 すべての iOS デバイスではレンズの絞りが固定されているため、露出の調整に使用できる値は、シャッター速度と ISO の 2 つだけです。

連続自動露出のしくみ

手動露出のしくみを学習する前に、iOS デバイスでの連続自動露出のしくみを理解することをお勧めします。

iOS デバイスでの継続自動露出のしくみ

1 つ目は自動露出ブロックで、理想的な露出を計算する役割があり、これに測定統計が継続的に供給されます。この情報を使用して、シーンの照度が十分になるように、ISO とシャッター速度の最適な組み合わせが計算されます。 このサイクルは AE ループと呼ばれます。

ロックされた露出のしくみ

次に、iOS デバイスでのロックされた露出のしくみを調べてみましょう。

iOS デバイスでの固定露出のしくみ

ここでも、最適な iOS と Duration の値を計算しようとしている自動露出ブロックがあります。 ただし、このモードでは、AE ブロックは測定統計エンジンから切断されます。

既存の露出コントロール

iOS 7 以降では、ExposureMode プロパティを使用して、次の既存の露出コントロールを指定します。

  • AVCaptureExposureModeLocked - シーンを 1 回サンプリングし、シーン全体でこれらの値を使用します。
  • AVCaptureExposureModeContinuousAutoExposure - シーンを継続的にサンプリングして、照度が適切であることを確認します。

ExposurePointOfInterest を使用して、公開するターゲット オブジェクトを選択してシーンを公開できます。また、アプリケーションは AdjustingExposure プロパティを監視して、露出が調整されるタイミングを確認できます。

iOS 8 の新しい露出コントロール

iOS 7 以降で既に提供されている機能に加えて、次の機能を使用して iOS 8 での露出を制御できるようになりました。

  • 完全に手動のカスタム露出。
  • 取得、設定、キー値観察 IOS とシャッター速度 (Duration)。

上記の機能を実装するために、新しい AVCaptureExposureModeCustom モードが追加されました。 カメラがカスタム モードの場合は、次のコードを使用して Exposure Duration と ISO を調整できます。

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

Auto モードと Locked モードでは、アプリケーションは次のコードを使用して、自動露出ルーチンのバイアスを調整できます。

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 をアプリケーションのストーリーボードに追加し、次のように構成できます。

UIViewController をアプリケーションのストーリーボードに追加し、次に示すように手動露出の例として構成できます。

このビューには、次の主要な要素が含まれています。

  • ビデオ フィードを表示する UIImageView
  • フォーカス モードを Automatic から Locked に変更する UISegmentedControl
  • Offset、Duration、ISO、Bias を表示および更新する 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. カメラを Auto モードにすると、カメラが露出を調整するにつれて、スライダーが自動的に移動します。

    カメラが露出を調整すると、スライダーが自動的に移動します

  7. [Locked] セグメントをタップし、[Bias] スライダーをドラッグして、自動露出のバイアスを手動で調整します。

    自動露出のバイアスを手動で調整する

  8. [Custom] セグメントをタップし、[Duration] スライダーと [ISO] スライダーをドラッグして、露出を手動で制御します。

    [期間] スライダーと [ISO] スライダーをドラッグして、露出を手動で制御する

  9. アプリケーションを停止します。

上記のコードでは、カメラが Automatic モードのときに露出設定を監視する方法と、スライダーを使用して、Locked モードまたは Custom モードのときに露出を制御する方法を示しています。

手動ホワイト バランス

ホワイト バランス コントロールを使用すると、ユーザーは画像内の色バランスを調整して、よりリアルに見せられます。 光源によって色温度が異なるため、画像のキャプチャに使用するカメラ設定を調整して、これらの違いを補正する必要があります。 繰り返しになりますが、ユーザーがホワイト バランスを制御できるようにすることで、自動ルーチンでは芸術的効果を実現できない専門的な調整が行えます。

手動ホワイト バランス調整を示すサンプル画像

たとえば、日中の光は青みがかったキャストを持っているのに対し、電灯や白熱灯は暖色の黄色からオレンジ色の色合いを持っています。 (紛らわしいことに、寒色が、暖色よりも高い色温度を持っています。色温度は物理的な尺度であり、知覚的なものではありません)。

人間の知覚は色温度の差を補うことに長けており、これはカメラにはできないことです。 カメラは、色の違いを調整するように反対のスペクトルの色をブーストすることで機能します。

新しい iOS 8 Exposure API を使用すると、アプリケーションはプロセスを制御し、カメラのホワイト バランス設定をきめ細かく制御できます。

ホワイト バランスのしくみ

IOS 8 アプリケーションでのホワイト バランスの制御の詳細について説明する前に、 ホワイト バランスのしくみをざっと見ていきましょう。

色知覚の研究では、「CIE 1931 RGB 色空間と CIE 1931 XYZ 色空間」は、数学的に定義された最初の色空間です。 これらは 1931 年に国際照明委員会 (CIE) によって作成されました。

CIE 1931 RGB 色空間と CIE 1931 XYZ 色空間

上のグラフは、人間の目に見えるすべての色を、深い青から明るい緑へ、明るい赤まで示しています。 上のグラフに示すように、ダイアグラム上の任意のポイントを X と Y の値でプロットできます。

グラフに表示されているように、グラフにプロットできる X 値と Y 値は人間の視覚の範囲外であり、その結果、これらの色はカメラでは再現できません。

上のグラフの小さい曲線は、色温度 (単位はケルビン度) を表すプランキアン軌跡と呼ばれ、より高い数値が青色の側 (高)、より低い数値が赤色の側 (低) に表されます。 これらは、一般的な照明の状況に役立ちます。

混合照明条件では、必要な変更を加えるために、ホワイト バランス調整がプランキアン軌跡から逸脱する必要があります。 このような状況では、調整を CIE スケールの緑側または赤/マゼンタ側にシフトする必要があります。

iOS デバイスは、反対の色ゲインをブーストすることで、色キャストを補正します。 たとえば、シーンに青が多すぎる場合、補正のために赤のゲインがブーストされます。 これらのゲイン値は、デバイスに依存するように特定のデバイス用に調整されます。

既存のホワイト バランス コントロール

iOS 7 以降では、WhiteBalanceMode プロパティを介して、次の既存のホワイト バランス コントロールが提供されています。

  • AVCapture WhiteBalance ModeLocked - シーンを 1 回サンプリングし、シーン全体でこれらの値を使用します。
  • AVCapture WhiteBalance ModeContinuousAutoExposure - シーンを継続的にサンプリングして、バランスが適切であるようにします。

また、アプリケーションは AdjustingWhiteBalance プロパティを監視して、露出が調整されるタイミングを確認できます。

iOS 8 の新しいホワイト バランス コントロール

iOS 7 以降で既に提供されている機能に加えて、次の機能を使用して、iOS 8 のホワイト バランスを制御できるようになりました。

  • デバイス RGB ゲインの完全に手動のコントロール。
  • デバイス RGB ゲインの取得、設定、キー値観察。
  • グレー カードを使用したホワイト バランスのサポート。
  • デバイスに依存しない色空間との間の変換ルーチン。

上記の機能を実装するために、AVCaptureWhiteBalanceGain 構造体が次のメンバーと共に追加されました。

  • RedGain
  • GreenGain
  • BlueGain

現在、ホワイト バランスの最大ゲインは 4 であり、MaxWhiteBalanceGain プロパティから準備できます。 したがって、法的範囲は現在 1 から MaxWhiteBalanceGain (4) です。

DeviceWhiteBalanceGains プロパティを使用して、現在の値を確認できます。 SetWhiteBalanceModeLockedWithDeviceWhiteBalanceGains を使用して、カメラがロックされたホワイト バランス モードのときに、バランス ゲインを調整します。

変換ルーチン

iOS 8 に変換ルーチンが追加され、デバイスに依存しない色空間との間の変換に役立ちます。 変換ルーチンを実装するために、AVCaptureWhiteBalanceChromaticityValues 構造体が次のメンバーと共に追加されました。

  • X - 0 から 1 までの値です。
  • Y - 0 から 1 までの値です。

次のメンバーを含む AVCaptureWhiteBalanceTemperatureAndTintValues 構造体も追加されました。

  • Temperature - ケルビン度単位での浮動小数点値です。
  • Tint - 緑またはマゼンタから 0 から 150 の数値で、正の値は緑の方向、負の値はマゼンタの方向にオフセットします。

CaptureDevice.GetTemperatureAndTintValues メソッドと CaptureDevice.GetDeviceWhiteBalanceGains メソッドを使用して、温度と濃淡、色度、RGB ゲインの色空間を変換します。

Note

変換ルーチンは、変換する値がプランキアン軌跡に近いほど正確です。

グレー カードのサポート

Apple は、グレー ワールドという用語を使用して、iOS 8 に組み込まれているグレー カードのサポートを参照します。 これにより、ユーザーは、フレーム中心の少なくとも 50% をカバーし、それを使用してホワイト バランスを調整する物理的なグレー カードにフォーカスを合わせられます。 グレー カードの目的は、ニュートラルに見えるホワイト バランスを達成することです。

これは、ユーザーにカメラの前に物理的な灰色のカードを配置するように求め、GrayWorldDeviceWhiteBalanceGains プロパティを監視し、値が落ち着くまで待機することで、アプリケーションに実装できます。

その後、アプリケーションは、変更を適用するために、GrayWorldDeviceWhiteBalanceGains プロパティからの値を使用して、SetWhiteBalanceModeLockedWithDeviceWhiteBalanceGains メソッドのホワイト バランスのゲインをロックします。

ホワイト バランスを変更する前に、キャプチャ デバイスを構成用にロックする必要があります。

手動のホワイト バランスの例

一般的な AV キャプチャのセットアップ コードを配置すると、UIViewController をアプリケーションのストーリーボードに追加し、次のように構成できます。

UIViewController をアプリケーションのストーリーボードに追加し、手動のホワイト バランスの例として次に示すように構成できます。

このビューには、次の主要な要素が含まれています。

  • ビデオ フィードを表示する UIImageView
  • フォーカス モードを Automatic から Locked に変更する UISegmentedControl
  • 温度と濃淡を表示および更新する 2 つの 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. カメラを Auto モードにすると、カメラがホワイト バランスを調整するにつれて、スライダーが自動的に移動します。

    カメラがホワイト バランスを調整すると、スライダーが自動的に移動します

  9. [Locked] セグメントをタップし、[Temp] スライダーと [Tint] スライダーをドラッグして、ホワイト バランスを手動で調整します。

    [色温度] スライダーと [濃淡] スライダーをドラッグして、手動でホワイト バランスを調整する

  10. [Locked] セグメントを選択したまま、カメラの前に物理グレー カードを配置し、[Gray Card] ボタンをタップして、ホワイト バランスをグレー ワールドに調整します。

    [グレー度] ボタンをタップして、グレー ワールドに合わせてホワイト バランスを調整する

  11. アプリケーションを停止します。

上記のコードでは、カメラが Automatic モードのときにホワイト バランス設定を監視する方法、またはスライダーを使用して Locked モードのときにホワイト バランスを制御する方法を示しています。

ブラケット キャプチャ

ブラケット キャプチャは、上記の手動カメラ コントロールの設定に基づいており、アプリケーションはさまざまな方法で瞬間をキャプチャできます。

簡単に言うと、ブラケット キャプチャは、画像から画像へのさまざまな設定で撮影された静止画像のバーストです。

ブラケット キャプチャのしくみ

iOS 8 のブラケット キャプチャを使用すると、アプリケーションは一連の手動カメラ コントロールをプリセットし、1 つのコマンドを発行し、現在のシーンで手動プリセットごとに一連の画像を返すことができます。

ブラケット キャプチャの基本

ここでも、ブラケット キャプチャは、画像から画像へのさまざまな設定で撮影された静止画像のバーストです。 使用できるブラケット キャプチャの種類は次のとおりです。

  • 自動露出ブラケット - すべての画像にさまざまなバイアス量があります。
  • 手動露出ブラケット - すべての画像に、さまざまなシャッター速度 (Duration) と ISO 量があります。
  • 単純なバースト ブラケット - 連続して撮影された一連の静止画像です。

iOS 8 の新しいブラケット キャプチャ コントロール

すべてのブラケット キャプチャ コマンドは、AVCaptureStillImageOutput クラスに実装されます。 CaptureStillImageBracket メソッドを使用して、指定された設定の配列を持つ一連の画像を取得します。

設定を処理するために、次の 2 つの新しいクラスが実装されました。

  • AVCaptureAutoExposureBracketedStillImageSettings - 1 つのプロパティ ExposureTargetBias を持ち、自動露出ブラケットのバイアスを設定するために使用されます。
  • AVCaptureManualExposureBracketedStillImageSettings - 手動露出ブラケットのシャッター速度と ISO を設定するために使用される ExposureDurationISO の 2 つのプロパティがあります。

ブラケット キャプチャ コントロールの推奨処理と非推奨処理

推奨

iOS 8 でブラケット キャプチャ コントロールを使用する場合に実行すべき処理の一覧を次に示します。

  • PrepareToCaptureStillImageBracket メソッドを呼び出して、最悪の場合のキャプチャ状況に備えてアプリを準備します。
  • サンプル バッファーが同じ共有プールから取得されると仮定します。
  • 前の準備呼び出しによって割り当てられたメモリを解放するには、もう一度 PrepareToCaptureStillImageBracket を呼び出し、これに 1 つのオブジェクトの配列を送信します。

やってはいけないこと

次に、iOS 8 でブラケット キャプチャ コントロールを使用する場合に実行すべきでない処理の一覧を示します。

  • ブラケット キャプチャ設定の種類を 1 つのキャプチャに混在させないでください。
  • 1 つのキャプチャで複数の MaxBracketedCaptureStillImageCount イメージを要求しないでください。

ブラケット キャプチャの詳細

iOS 8 でブラケット キャプチャを使用する場合は、次の詳細を考慮する必要があります。

  • ブラケット設定は、AVCaptureDevice 設定を一時的にオーバーライドします。
  • フラッシュと静止画像の安定化の設定は無視されます。
  • すべての画像で同じ出力形式 (jpeg、png など) を使用する必要があります。
  • ビデオ プレビューで、フレームが欠落する場合があります。
  • ブラケット キャプチャは、iOS 8 と互換性のあるすべてのデバイスでサポートされています。

この情報を念頭に置いて、iOS 8 でブラケット キャプチャを使用する例を見てみましょう。

ブラケット キャプチャの例

一般的な AV キャプチャのセットアップ コードを配置すると、UIViewController をアプリケーションのストーリーボードに追加し、次のように構成できます。

UIViewController をアプリケーションのストーリーボードに追加し、ブラケット キャプチャの例として次に示すように構成できます。

このビューには、次の主要な要素が含まれています。

  • ビデオ フィードを表示する UIImageView
  • キャプチャの結果を表示する 3 つの UIImageViews
  • ビデオ フィードと結果ビューを収容する UIScrollView
  • 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. シーンをフレームに収め、[Capture Bracket] ボタンをタップします。

    シーンをフレームに収め、[Capture Bracket] ボタンをタップします

  9. 右から左にスワイプすると、ブラケット キャプチャによって撮影された 3 つの画像が表示されます。

    右から左にスワイプすると、ブラケット キャプチャによって撮影された 3 つの画像が表示されます

  10. アプリケーションを停止します。

上記のコードは、iOS 8 で自動露出ブラケット キャプチャを構成して取得する方法を示しています。

まとめ

この記事では、iOS 8 で提供される新しい手動カメラ コントロールの概要について説明し、その動作と動作のしくみの基本について説明しました。 ここでは、手動フォーカス、手動露出、手動ホワイト バランスの例を示しました。 最後に、前に説明した手動カメラ コントロールを使用して、ブラケット キャプチャを実行する例を示しました