Avcapturedevice Camera Zoom

j'ai un AVCaptureSession simple en cours d'exécution pour obtenir un flux d'appareil photo dans mon application et prendre des photos. Comment puis-je implémenter la fonctionnalité 'pinch to zoom' en utilisant un UIGestureRecognizer pour la caméra?

16
demandé sur The Kraken 2012-04-19 06:42:54

7 réponses

la réponse acceptée est en fait périmée et je ne suis pas sûr qu'elle prendra la photo de l'image agrandie. Il existe une méthode pour zoomer comme bcattle réponse dit. Le problème de sa réponse est qu'il ne prend pas en charge le fait que l'utilisateur peut zoomer, puis redémarrez à partir de cette position de zoom. Sa solution créera une sorte de sauts qui ne sont pas vraiment élégants.

la façon la plus facile et élégante de faire ceci est d'utiliser la vitesse de pincement geste.

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

j'ai trouvé que l'ajout de la fonction arctan à la vitesse facilitera un peu l'effet zoom in zoom out. Ce n'est pas exactement parfait, mais l'effet est assez bonne pour les besoins. Il pourrait probablement être une autre fonction pour faciliter le zoom quand il atteint presque 1.

NOTE: aussi, l'échelle d'un geste de pincement va de 0 à infini avec 0 à 1 pincement (zoom arrière) et 1 à Infini pincement (zoom arrière). Pour obtenir une bonne zoom avant zoom arrière effet avec cela, vous auriez besoin d'avoir une équation mathématique. La vitesse est en fait de-infinie à infinie avec 0 étant le point de départ.

EDIT: crash fixe sur l'exception de portée. Grâce à @garafajon!

38
répondu Gabriel Cartier 2017-05-23 12:34:15

Beaucoup ont essayé de le faire en définissant la propriété transform sur le calque CGAffineTransformMakeScale(gesture.scale.x, gesture.scale.y); Voir ici pour une mise en œuvre complète de pinch-to-zoom.

6
répondu CodaFi 2013-01-09 22:14:57

depuis iOS 7 vous pouvez régler le zoom directement avec le videoZoomFactorAVCaptureDevice.

attachez le scale propriété de l' UIPinchGestureRecognizervideoZoomFactor avec une constante d'échelle. Cela vous permettra de varier la sensibilité au goût:

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

Notez que AVCaptureDevice, ainsi que tout ce qui est lié à AVCaptureSession, n'est pas sans fil. Donc tu ne veux probablement pas faire ça depuis la file d'attente principale.

4
répondu bcattle 2015-04-07 21:39:01

Swift 4

Ajoutez une pinch geste recognizer à la vue la plus en avant et connectez-la à cette action ( pinchToZoom). captureDevice devrait être l'instance fournissant actuellement des données à la session de capture. pinchToZoom fournit un zoom lisse pour les deux et dispositifs de capture.

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

il sera utile de déclarer zoomFactor sur votre caméra ou vc. D'habitude, je le mets sur le même singleton. cela a une AVCaptureSession. Ceci agira comme une valeur par défaut pour videozoofactor.

var zoomFactor: Float = 1.0
4
répondu jnblanchard 2018-03-21 15:54:57

dans la version swift, vous pouvez zoomer en passant simplement un nombre gradué sur videozoonfactor. Le code suivant dans uipinchgesturerecognizer handler résoudra le problème.

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
}

ici, pivotPinchScale est une propriété CGFloat déclarée dans votre controller quelque part.

Vous pouvez également vous référer au projet suivant pour voir comment la caméra fonctionne avec UIPinchGestureRecognizer. https://github.com/DragonCherry/CameraPreviewController

1
répondu DragonCherry 2017-01-13 04:38:28

je suis parti de la solution de @Gabriel Cartier (merci). Dans mon code, j'ai préféré utiliser le smoother rampToVideoZoomFactor et une façon plus simple de calculer le facteur d'échelle de l'appareil.

(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
répondu Nicola Vacca 2018-03-26 06:42:19

j'utilise iOS SDK 8.3 et le cadre AVfoundation et pour moi j'utilise la méthode suivante travaillée pour:

nameOfAVCaptureVideoPreviewLayer.affineTransform = CGAffineTransformMakeScale(scaleX, scaleY) 

Pour sauvegarder l'image avec la même échelle, j'ai utilisé la méthode suivante:

nameOfAVCaptureConnection.videoScaleAndCropFactor = factorNumber; 

Le code soufflet est pour obtenir l'image de l'échelle

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

            NSData *imageData = [AVCaptureStillImageOutput  jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
            UIImage *image = [UIImage imageWithData:imageData];
}
}];
-1
répondu Emanuel Martinez Vazquez 2015-07-14 06:22:57