[iOS] AVFoundation のカメラでフォーカスと露出をタッチで合わせる

2011年12月2日金曜日 | Published in | 0 コメント

このエントリーをはてなブックマークに追加

AVFoundation を使い、タッチでフォーカスと露出を決められるカスタムカメラを作る。今日の主題はタッチフォーカス&露出なのでプレビューを表示するだけで撮影(画像の保存)は実装していない。

サンプル


まずはサンプルから。起動すると真ん中に四角が表示される。

この状態で任意の場所をタッチするとそこにフォーカスと露出が合う。

手前を合わせてみた。


実装 - プレビュー


カメラ入力をプレビュー表示する部分の説明は今回割愛する。この辺りは下記のドキュメントが詳しい。ひと通り読めば簡単に実装できる。

AV Foundation プログラミングガイド

もしくは



など。後者の本は iOS4 となっているが iOS5 でも十分に役立つ情報が掲載されている。おすすめの本。


実装 - フォーカスと露出


任意の点にフォーカスと露出をあわせるプロパティが用意されている。
AVCaptureDevice.h

@property(nonatomic) CGPoint focusPointOfInterest;
@property(nonatomic) CGPoint exposurePointOfInterest;
基本的にはここにフォーカスもしくは露出を合わせたい座標を入れればいい。

ただしこのプロパティに設定する値は次のルールに従う必要がある。

(1) 0.0〜1.0 に正規化した値
(2) ランドスケープ(横向き/ホームボタン右)の時の左上を原点とする座標系

「AVFoundation プログラミングガイド(PDF)」によれば次のように解説されている。
ホームボタンが右側になる横長モードでは、{0,0}が画像領域の左上を表し、{1,1}が右下を表します。これ
は、デバイスが縦長モードであっても同じです。


つまり通常のポートレイト(縦向き/ホームボタン下)の状態の時に画面上のタッチ位置をこれらに反映させるには、(1)の正規化と(2)の座標系への変換が必要になる。

(1)の正規化は単純に大きさで位置座標を割ってやれば良い。 (x/width, y/height)

ポートレイトの座標系(左上が原点)を使っている時のそれぞれの座標系の関係はこんな感じ。

x と y を入れ替えて、かつ y軸の方向に反転してやればいい。これは(1)の正規化が済んでいれば簡単にできる。

サンプルでのコードはこんな感じ。
- (void)setPoint:(CGPoint)p
{
    CGSize viewSize = self.view.bounds.size;
    CGPoint pointOfInterest = CGPointMake(p.y / viewSize.height,
                                          1.0 - p.x / viewSize.width);

フォーカスの場合は、この値を focusPointOfInterest へ渡し、focusMode を設定すれば良い。こんな感じ。
if ([videoCaptureDevice isFocusPointOfInterestSupported] &&
            [videoCaptureDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
            videoCaptureDevice.focusPointOfInterest = pointOfInterest;
            videoCaptureDevice.focusMode = AVCaptureFocusModeAutoFocus;
        }

露出の方は、exposurePointOfInterest 渡すだけでは駄目で、もう少し手間が必要となる。
if ([videoCaptureDevice isExposurePointOfInterestSupported] &&
            [videoCaptureDevice isExposureModeSupported:
                  AVCaptureExposureModeContinuousAutoExposure]){
            self.adjustingExposure = YES;
            videoCaptureDevice.exposurePointOfInterest = pointOfInterest;
            videoCaptureDevice.exposureMode =
                  AVCaptureExposureModeContinuousAutoExposure;
        }
ここまではフォーカスとほぼ同じ。これに加えて露出の場合は、exposurePointOfInterest設定後に AVCaptureDevice の adjustingExposure プロパティを監視しこの値が NO(つまり露出合わせが終了)になったタイミングで exposureMode を AVCaptureExposureModeLocked にする必要がある。

- (void)viewDidLoad
{
   :
            [videoCaptureDevice addObserver:self
                                 forKeyPath:@"adjustingExposure"
                                    options:NSKeyValueObservingOptionNew
                                    context:nil];
   :
監視を設定しておき値が変化したら処理をする。
- (void)observeValueForKeyPath:(NSString *)keyPath
  ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (!self.adjustingExposure) {
        return;
    }
    
 if ([keyPath isEqual:@"adjustingExposure"]) {
  if ([[change objectForKey:NSKeyValueChangeNewKey] boolValue] == NO) {
            self.adjustingExposure = NO;
            AVCaptureDevice* videoCaptureDevice =
            [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
            
   NSError *error = nil;
   if ([videoCaptureDevice lockForConfiguration:&error]) {
     [videoCaptureDevice setExposureMode:AVCaptureExposureModeLocked];
    [videoCaptureDevice unlockForConfiguration];
   }
  }
 }
}

試してみよう。まずは右にある MacBook Pro の明るい部分に合わせた。

次に手前の暗い 3GS。

うまくいったようだ。


ソースコード


GitHub からどうぞ。
CameraSample at 2011-12-02 from xcatsan/iOS-Sample-Code - GitHub


参考情報


iPhone4でサポートされてない露出モードのエラーが出たけど - Debian GNU/Linux 3.1 on PowerMac G4

露出の位置指定はここの情報が参考になった(紹介されているコードは若干不備がある 2011-12-03追記:記事著者の方が修正してくれました。素早い!)。また毎回値監視をつけたり外したりするのは管理しずらいのでサンプルでは viewDidLoad で監視を開始している(監視外しは今見たら入れ忘れてた→dealloc, viewidiUnloadなどに入れておく)。


人気の投稿(過去 30日間)