AVCaptureDevice Camera Zoom

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


person The Kraken    schedule 19.04.2012    source источник
comment
Где вы добавляете жест щипка?   -  person Dalvik    schedule 01.10.2017


Ответы (9)


Принятый ответ на самом деле устарел, и я не уверен, что он действительно сделает фотографию увеличенного изображения. Существует способ увеличить масштаб, как говорит ответ 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);
        }
    }
}

Я обнаружил, что добавление функции арктангенса к скорости немного облегчит эффект увеличения и уменьшения масштаба. Это не совсем идеально, но эффект достаточно хорош для нужд. Вероятно, может быть другая функция, позволяющая уменьшить масштаб, когда он почти достигает 1.

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

EDIT: исправлен сбой при исключении диапазона. Спасибо @garafajon!

person Gabriel Cartier    schedule 03.07.2015
comment
Спасибо. Используйте это, чтобы не получать исключение диапазона: videoDevice.videoZoomFactor = MAX(1.0, MIN(желаемыйZoom,videoDevice.activeFormat.videoMaxZoomFactor)); - person garafajon; 01.10.2015
comment
Имейте в виду, что скорость может возвращать nan. Возможно, вы захотите проверить это перед тем, как перейти к вычислениям: if (isnan(pinchRecognizer.velocity)) { return; } - person Masa; 15.03.2016
comment
видеоZoomFactor такой же, как зум? - person khunshan; 12.01.2017
comment
Где я должен добавить жест щипка? @гарафаджон - person Dalvik; 01.10.2017
comment
Пожалуйста, просмотрите мой ответ, чтобы получить более простой способ обработки уровня масштабирования камеры с помощью распознавателя пинча > - person Anton K; 05.11.2019

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

person CodaFi    schedule 19.04.2012
comment
Спасибо за ответ. Но как простое изменение масштаба предварительного просмотра видео фактически меняет масштаб аппаратного обеспечения камеры? - person The Kraken; 19.04.2012
comment
Это не так. Вот почему даже камера Apple не «масштабирует». Это всего лишь CGAffines и причудливая обрезка. - person CodaFi; 19.04.2012
comment
Да, камера просто использует цифровой зум. Но что еще я должен сделать, помимо масштабирования предварительного просмотра видео, чтобы изображение, записываемое на диск, действительно увеличивалось? - person The Kraken; 19.04.2012
comment
См. здесь. Просто знайте, что ни одно устройство iOS не имеет аппаратного масштабирования, поэтому для этого требуется немного больше математики, чем ответ, на который я ссылался. - person CodaFi; 19.04.2012
comment
Большое спасибо. Я заметил, что в ответе на связанный вопрос говорится, что приложение вылетало, когда масштаб был максимальным из-за памяти. Это то, о чем мне нужно беспокоиться в рамках ARC? - person The Kraken; 19.04.2012
comment
Я думаю, что вопрос немного устарел, так что нет. Он должен работать идеально. - person CodaFi; 19.04.2012
comment
Начиная с iOS 7 на самом деле есть способ увеличить изображение, как сказал @bcattle. Проблема с его решением в том, что оно будет прыгать между щепоткой. Смотрите мой ответ для более плавного и правильного способа сделать это. - person Gabriel Cartier; 03.07.2015
comment
Ссылка не работает - person aheze; 22.04.2021

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

person bcattle    schedule 07.04.2015

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 на вашей камере или vc. Я обычно ставлю его на тот же синглтон, что и AVCaptureSession. Это будет действовать как значение по умолчанию для videoZoomFactor устройства захвата.

var zoomFactor: Float = 1.0
person jnblanchard    schedule 24.12.2017
comment
Привет, переменная, которую вы определили здесь, мне непонятна. Как новичок. Можете ли вы сказать мне, что такое переменная pinch в вашем коде? Спасибо! - person Karthik Kannan; 21.03.2018
comment
Просто глядя на это снова, я думаю, что щепотка должна быть отправителем. Или параметр отправителя можно пережать. - person jnblanchard; 21.03.2018
comment
Пришлось, var zoomFactor: CGFloat = 1.0 - person fonz; 15.05.2018

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

person DragonCherry    schedule 13.01.2017

Я начал с решения @Gabriel Cartier (спасибо). В своем коде я предпочитаю использовать более плавный метод рамптовидеозумфактор и более простой способ вычисления коэффициента масштабирования устройства.

(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);
        }
    }
}
person Nicola Vacca    schedule 25.03.2018

Существует более простой способ управления уровнем масштабирования камеры с помощью распознавателя щипков. Единственное, что вам нужно сделать, это взять 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
    }
}
person Anton K    schedule 05.11.2019

на основе ответа @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);
            }
        }
    }
}
person side    schedule 16.04.2019

Я использую 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];
}
}];
person Emanuel Martinez Vazquez    schedule 14.07.2015