Avcapturedevice Камеры Зум
У меня есть простой AVCaptureSession работает, чтобы получить канал камеры в моем приложении и делать фотографии. Как я могу реализовать функциональность "pinch to zoom" с помощью UIGestureRecognizer
для фотоаппарата?
7 ответов
принятый ответ на самом деле устарел, и я не уверен, что он действительно сделает фотографию увеличенного изображения. Существует метод увеличения масштаба, как говорит bcattle answer. Проблема его ответа заключается в том, что он не берет на себя ответственность за то, что пользователь может увеличить, а затем перезапустить с этой позиции масштабирования. Его решение создаст какие-то прыжки, которые не очень изящны.
самый простой и элегантный способ сделать это-использовать скорость щипка жест.
-(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!
многие пытались сделать это, установив для свойства transform слоя значение CGAffineTransformMakeScale(gesture.scale.x, gesture.scale.y);
См.здесь для полноценной реализации pinch-to-zoom.
С 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
, не является потокобезопасным. Поэтому вы, вероятно, не хотите делать это из главной очереди.
Swift 4
Добавьте распознаватель жестов pinch в самый передний вид и подключите его к этому действию (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. Это будет действовать как значение по умолчанию для captureDevice videoZoomFactor.
var zoomFactor: Float = 1.0
в версии swift вы можете увеличивать/уменьшать масштаб, просто передавая масштабированное число на 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
Я начал с решения @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);
}
}
}
Я использую iOS SDK 8.3 и AVFoundation framework, и для меня работает следующий метод:
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];
}
}];