iphone - AVCaptureDevice Camera Zoom
objective-c ios (7)
Tengo una sesión de AVCaptureSession simple para obtener una imagen de la cámara en mi aplicación y tomar fotos. ¿Cómo puedo implementar la funcionalidad ''pellizcar para ampliar'' usando un UIGestureRecognizer
para la cámara?
Comencé desde la solución de @Gabriel Cartier (gracias). En mi código, he preferido usar el rampo de video más suave y una forma más sencilla de calcular el factor de escala del dispositivo.
(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);
}
}
}
Desde iOS 7 puede establecer el zoom directamente con la propiedad AVCaptureDevice
de AVCaptureDevice
.
UIPinchGestureRecognizer
la propiedad de scale
de UIPinchGestureRecognizer
al videoZoomFactor
con una constante de escala. Esto te permitirá variar la sensibilidad al gusto:
-(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);
}
}
}
Tenga en cuenta que AVCaptureDevice
, junto con todo lo relacionado con AVCaptureSession
, no es seguro para subprocesos. Así que probablemente no quieras hacer esto desde la cola principal.
En la versión Swift, puede acercar / alejar simplemente pasando un número escalado en videoZoomFactor. El siguiente código en el controlador UIPinchGestureRecognizer resolverá el problema.
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
}
Aquí, pivotPinchScale es una propiedad CGFloat que se declaró en su controlador en algún lugar.
También puede consultar el siguiente proyecto para ver cómo funciona la cámara con UIPinchGestureRecognizer. https://github.com/DragonCherry/CameraPreviewController
Estoy usando iOS SDK 8.3 y el marco de AVfoundation y para mí utilizo el siguiente método para:
nameOfAVCaptureVideoPreviewLayer.affineTransform = CGAffineTransformMakeScale(scaleX, scaleY)
Para guardar la imagen con la misma escala utilicé el siguiente método:
nameOfAVCaptureConnection.videoScaleAndCropFactor = factorNumber;
El siguiente código es para obtener la imagen en la escala.
[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
if(imageDataSampleBuffer != NULL){
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
UIImage *image = [UIImage imageWithData:imageData];
}
}];
La respuesta aceptada está realmente desactualizada y no estoy seguro de que realmente tome la foto de la imagen ampliada. Hay un método para acercar como dice bcattle respuesta. El problema de su respuesta es que no se hace cargo del hecho de que el usuario puede acercar y luego reiniciar desde esa posición de zoom. Su solución creará una especie de saltos que no son realmente elegantes.
La forma más fácil y elegante de hacer esto es usar la velocidad del gesto de pellizco.
-(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);
}
}
}
Descubrí que agregar la función arctan a la velocidad facilitará un poco el efecto de zoom en el alejamiento. No es exactamente perfecto, pero el efecto es lo suficientemente bueno para las necesidades. Probablemente podría haber otra función para facilitar el alejamiento cuando casi llega a 1.
NOTA : Además, la escala de un gesto de pellizco va de 0 a infinito, con 0 a 1 pinchando (alejar) y de 1 a infinito pinchando (acercar). Para obtener un buen efecto de alejar o alejar la imagen con esto, necesitarás una ecuación matemática. La velocidad es en realidad de infinita a infinita, siendo 0 el punto de partida.
EDITAR : Solucionado el fallo en el rango de excepción. Gracias a @garafajon !
Muchos han intentado hacer esto estableciendo la propiedad de transformación en la capa a CGAffineTransformMakeScale(gesture.scale.x, gesture.scale.y);
Vea here para una implementación completa de pinch-to-zoom.
Swift 4
Agregue un reconocedor de gestos de pellizco a la vista frontal y conéctelo a esta acción ( pinchToZoom ). captureDevice debe ser la instancia que actualmente proporciona información para la sesión de captura. pinchToZoom proporciona un zoom suave para los dispositivos de captura frontal y posterior .
@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
}
}
Será útil declarar zoomFactor en tu cámara o vc. Normalmente lo pongo en el mismo singleton que tiene AVCaptureSession. Esto actuará como un valor predeterminado para el videoZoomFactor de captureDevice .
var zoomFactor: Float = 1.0