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?
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!
depuis iOS 7 vous pouvez régler le zoom directement avec le videoZoomFactor
AVCaptureDevice
.
attachez le scale
propriété de l' UIPinchGestureRecognizer
videoZoomFactor
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.
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
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
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);
}
}
}
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];
}
}];