web-dev-qa-db-ja.com

AVCaptureDeviceカメラズーム

アプリでカメラフィードを取得して写真を撮るために、単純なAVCaptureSessionを実行しています。カメラにUIGestureRecognizerを使用して、「ピンチしてズーム」機能を実装するにはどうすればよいですか?

19
The Kraken

受け入れられた答えは実際には時代遅れであり、拡大された画像の写真を実際に撮るかどうかはわかりません。 bcattleの答えが言うようにズームインする方法があります。彼の答えの問題は、ユーザーがズームインしてそのズーム位置から再開できるという事実を担当していないということです。彼の解決策は、本当にエレガントではないある種のジャンプを作成します。

これを行う最も簡単でエレガントな方法は、ピンチジェスチャの速度を使用することです。

-(void) handlePinchToZoomRecognizer:(UIPinchGestureRecognizer*)pinchRecognizer {
    const CGFloat pinchVelocityDividerFactor = 5.0f;

    if (pinchRecognizer.state == UIGestureRecognizerStateChanged) {
        NSError *error = nil;
        if ([videoDevice lockForConfiguration:&error]) {
            CGFloat desiredZoomFactor = device.videoZoomFactor + atan2f(pinchRecognizer.velocity, pinchVelocityDividerFactor);
            // Check if desiredZoomFactor fits required range from 1.0 to activeFormat.videoMaxZoomFactor
            device.videoZoomFactor = MAX(1.0, MIN(desiredZoomFactor, device.activeFormat.videoMaxZoomFactor));
            [videoDevice unlockForConfiguration];
        } else {
            NSLog(@"error: %@", error);
        }
    }
}

速度にarctan関数を追加すると、ズームイン、ズームアウトの効果が少し緩和されることがわかりました。それは完全ではありませんが、効果はニーズに十分に適しています。ズームアウトがほぼ1に達したときに、ズームアウトを簡単にする別の機能がある可能性があります。

[〜#〜] note [〜#〜]:また、ピンチジェスチャのスケールは0から無限になり、0から1がピンチインします。 (ズームアウト)および1から無限にピンチアウト(ズームイン)。これで適切なズームインズームアウト効果を得るには、数式が必要です。速度は実際には-無限から無限であり、0が開始点です。

[〜#〜] edit [〜#〜]:範囲例外でのクラッシュを修正しました。ありがとう @ garafajon

43
Gabriel Cartier

多くの人が、レイヤーの変換プロパティをCGAffineTransformMakeScale(gesture.scale.x, gesture.scale.y);に設定することでこれを行おうとしました。ピンチツーズームの本格的な実装については、 ここ を参照してください。

6
CodaFi

IOS 7以降、videoZoomFactorの-​​ AVCaptureDevice プロパティを使用してズームを直接設定できます。

scaleUIPinchGestureRecognizerプロパティをスケーリング定数を使用してvideoZoomFactorに関連付けます。これにより、味覚に対する感度を変えることができます。

-(void) handlePinchToZoomRecognizer:(UIPinchGestureRecognizer*)pinchRecognizer {
    const CGFloat pinchZoomScaleFactor = 2.0;

    if (pinchRecognizer.state == UIGestureRecognizerStateChanged) {
        NSError *error = nil;
        if ([videoDevice lockForConfiguration:&error]) {
            videoDevice.videoZoomFactor = 1.0 + pinchRecognizer.scale * pinchZoomScaleFactor;
            [videoDevice unlockForConfiguration];
        } else {
            NSLog(@"error: %@", error);
        }
    }
}

AVCaptureDeviceは、AVCaptureSessionに関連する他のすべてのものと同様に、スレッドセーフではないことに注意してください。したがって、メインキューからこれを実行することはおそらく望ましくありません。

5
bcattle

Swift 4
ピンチジェスチャレコグナイザーを最前面のビューに追加し、このアクションに接続します(pinchToZoom)。 captureDeviceは、キャプチャセッションに現在入力を提供しているインスタンスである必要があります。 pinchToZoomは、両方のフロント&バックキャプチャデバイスにスムーズなズームを提供します。

  @IBAction func pinchToZoom(_ pinch: UIPinchGestureRecognizer) {

    guard let device = captureDevice else { return }

    func minMaxZoom(_ factor: CGFloat) -> CGFloat { return min(max(factor, 1.0), device.activeFormat.videoMaxZoomFactor) }

    func update(scale factor: CGFloat) {
      do {
        try device.lockForConfiguration()
        defer { device.unlockForConfiguration() }
        device.videoZoomFactor = factor
      } catch {
        debugPrint(error)
      } 
    }

    let newScaleFactor = minMaxZoom(pinch.scale * zoomFactor)

    switch sender.state {
      case .began: fallthrough
      case .changed: update(scale: newScaleFactor)
      case .ended:
        zoomFactor = minMaxZoom(newScaleFactor)
        update(scale: zoomFactor)
     default: break
   }
 }

カメラまたはVCでzoomFactorを宣言すると便利です。私は通常、AVCaptureSessionと同じシングルトンに配置します。これは、captureDeviceのvideoZoomFactorのデフォルト値として機能します。

var zoomFactor: Float = 1.0
4
jnblanchard

Swiftバージョンでは、videoZoomFactorでスケーリングされた数値を渡すだけでズームイン/ズームアウトできます。UIPinchGestureRecognizerハンドラーの次のコードで問題が解決します。

do {
    try device.lockForConfiguration()
    switch gesture.state {
    case .began:
        self.pivotPinchScale = device.videoZoomFactor
    case .changed:
        var factor = self.pivotPinchScale * gesture.scale
        factor = max(1, min(factor, device.activeFormat.videoMaxZoomFactor))
        device.videoZoomFactor = factor
    default:
        break
    }
    device.unlockForConfiguration()
} catch {
    // handle exception
}

ここで、pivotPinchScaleは、コントローラーのどこかで宣言されたCGFloatプロパティです。

次のプロジェクトを参照して、カメラがUIPinchGestureRecognizerでどのように機能するかを確認することもできます。 https://github.com/DragonCherry/CameraPreviewController

1
DragonCherry

@Gabriel Cartierのソリューションから始めました(ありがとう)。私のコードでは、よりスムーズなrampToVideoZoomFactorと、デバイスのスケールファクターを計算するためのより簡単な方法を使用することを好みました。

(IBAction) pinchForZoom:(id) sender forEvent:(UIEvent*) event {
    UIPinchGestureRecognizer* pinchRecognizer = (UIPinchGestureRecognizer *)sender;

    static CGFloat zoomFactorBegin = .0;
    if ( UIGestureRecognizerStateBegan == pinchRecognizer.state ) {
        zoomFactorBegin = self.captureDevice.videoZoomFactor;

    } else if (UIGestureRecognizerStateChanged == pinchRecognizer.state) {
        NSError *error = nil;
        if ([self.captureDevice lockForConfiguration:&error]) {

            CGFloat desiredZoomFactor = zoomFactorBegin * pinchRecognizer.scale;
            CGFloat zoomFactor = MAX(1.0, MIN(desiredZoomFactor, self.captureDevice.activeFormat.videoMaxZoomFactor));
            [self.captureDevice rampToVideoZoomFactor:zoomFactor withRate:3.0];

            [self.captureDevice unlockForConfiguration];
        } else {
            NSLog(@"error: %@", error);
        }
    }
}
1
Nicola Vacca

@Gabriel Cartierの回答に基づく:

- (void) cameraZoomWithPinchVelocity: (CGFloat)velocity {
    CGFloat pinchVelocityDividerFactor = 40.0f;
    if (velocity < 0) {
        pinchVelocityDividerFactor = 5.; //zoom in
    }

    if (_videoInput) {
        if([[_videoInput device] position] == AVCaptureDevicePositionBack) {
            NSError *error = nil;
            if ([[_videoInput device] lockForConfiguration:&error]) {
                CGFloat desiredZoomFactor = [_videoInput device].videoZoomFactor + atan2f(velocity, pinchVelocityDividerFactor);
                // Check if desiredZoomFactor fits required range from 1.0 to activeFormat.videoMaxZoomFactor
                CGFloat maxFactor = MIN(10, [_videoInput device].activeFormat.videoMaxZoomFactor);
                [_videoInput device].videoZoomFactor = MAX(1.0, MIN(desiredZoomFactor, maxFactor));
                [[_videoInput device] unlockForConfiguration];
            } else {
                NSLog(@"cameraZoomWithPinchVelocity error: %@", error);
            }
        }
    }
}
0
side

ピンチレコグナイザーでカメラのズームレベルを処理する簡単な方法があります。あなたがする必要がある唯一のことはcameraDevice.videoZoomFactorを取り、それをこのように.began状態の認識装置に設定することです

@objc private func viewPinched(recognizer: UIPinchGestureRecognizer) {
    switch recognizer.state {
        case .began:
            recognizer.scale = cameraDevice.videoZoomFactor
        case .changed:
            let scale = recognizer.scale
            do {
                 try cameraDevice.lockForConfiguration()
                 cameraDevice.videoZoomFactor = max(cameraDevice.minAvailableVideoZoomFactor, min(scale, cameraDevice.maxAvailableVideoZoomFactor))
                 cameraDevice.unlockForConfiguration()
            }
            catch {
                print(error)
            }
        default:
            break
    }
}
0
Anton K