Avcapturedevice Камеры Зум

У меня есть простой AVCaptureSession работает, чтобы получить канал камеры в моем приложении и делать фотографии. Как я могу реализовать функциональность "pinch to zoom" с помощью UIGestureRecognizer для фотоаппарата?

7 ответов


принятый ответ на самом деле устарел, и я не уверен, что он действительно сделает фотографию увеличенного изображения. Существует метод увеличения масштаба, как говорит bcattle answer. Проблема его ответа заключается в том, что он не берет на себя ответственность за то, что пользователь может увеличить, а затем перезапустить с этой позиции масштабирования. Его решение создаст какие-то прыжки, которые не очень изящны.

самый простой и элегантный способ сделать это-использовать скорость щипка жест.

-(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.

Примечание: кроме того, масштаб жеста щипка идет от 0 до бесконечности с 0 до 1 защемление (уменьшение) и 1 до бесконечности щипать (увеличение). Чтобы получить хорошее увеличение эффекта уменьшения масштаба с этим вам нужно будет иметь математическое уравнение. Скорость на самом деле от-бесконечна до бесконечности, причем 0 является начальной точкой.

редактировать: Исправлена ошибка при исключении диапазона. Спасибо @garafajon!


многие пытались сделать это, установив для свойства transform слоя значение CGAffineTransformMakeScale(gesture.scale.x, gesture.scale.y); См.здесь для полноценной реализации pinch-to-zoom.


С iOS 7 Вы можете установить масштаб непосредственно с помощью videoZoomFactor собственность AVCaptureDevice.

связать scale свойства UIPinchGestureRecognizer до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, не является потокобезопасным. Поэтому вы, вероятно, не хотите делать это из главной очереди.


Swift 4
Добавьте распознаватель жестов pinch в самый передний вид и подключите его к этому действию (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
   }
 }

будет полезно объявить zoomFactor на вашей камере или vc. Я обычно ставлю его на тот же синглтон, который имеет AVCaptureSession. Это будет действовать как значение по умолчанию для captureDevice videoZoomFactor.

var zoomFactor: Float = 1.0

в версии 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


Я начал с решения @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);
        }
    }
}

Я использую iOS SDK 8.3 и AVFoundation framework, и для меня работает следующий метод:

nameOfAVCaptureVideoPreviewLayer.affineTransform = CGAffineTransformMakeScale(scaleX, scaleY) 

для сохранения изображения в том же масштабе я использовал следующий метод:

nameOfAVCaptureConnection.videoScaleAndCropFactor = factorNumber; 

код ниже для получения изображения в масштабе

[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
        if(imageDataSampleBuffer != NULL){

            NSData *imageData = [AVCaptureStillImageOutput  jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
            UIImage *image = [UIImage imageWithData:imageData];
}
}];