Подтвердить что ты не робот

AVCaptureDevice Camera Zoom

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

Ответ 3

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

Ответ 4

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
   }
 }

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

var zoomFactor: Float = 1.0

Ответ 5

В быстрой версии вы можете увеличивать/уменьшать масштаб, просто передавая масштабированный номер на 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

Ответ 6

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

Ответ 7

   based on @Gabriel Cartier  answer :



- (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);
            }
        }
    }
}

Ответ 8

Я использую IOS SDK 8.3 и структуру AVfoundation и для меня, используя следующий метод, который работал для:

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];
}
}];