ios AVCaptureDevice 相机变焦
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10220958/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
AVCaptureDevice Camera Zoom
提问by The Kraken
I have a simple AVCaptureSession running to get a camera feed in my app and take photos. How can I implement the 'pinch to zoom' functionality using a UIGestureRecognizer
for the camera?
我有一个简单的 AVCaptureSession 正在运行以在我的应用程序中获取相机馈送并拍照。如何使用UIGestureRecognizer
相机的a 实现“捏合缩放”功能?
回答by Gabriel Cartier
The accepted answer is actually outdated and I'm not sure it will actually take the photo of the zoomed in image. There is a method to zoom in like bcattle answer says. The problem of his answer is that it does not take in charge the fact that the user can zoom in and then restart from that zoom position. His solution will create some kind of jumps that are not really elegant.
接受的答案实际上已经过时了,我不确定它是否真的会拍摄放大图像的照片。有一种方法可以像 bcattle answer 所说的那样放大。他的回答的问题在于,用户可以放大然后从该缩放位置重新开始这一事实并不负责。他的解决方案将创建某种不太优雅的跳跃。
The easiest and most elegant way of doing this is to use the velocity of the pinch gesture.
最简单、最优雅的方法是使用捏合手势的速度。
-(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);
}
}
}
I found that adding the arctan function to the velocity will ease the zoom in zoom out effect a bit. It is not exactly perfect but the effect is good enough for the needs. There could probably be another function to ease the zoom out when it almost reaches 1.
我发现将 arctan 函数添加到速度会稍微缓解放大缩小效果。它并不完全完美,但效果足以满足需求。当它几乎达到 1 时,可能还有另一个功能可以缓解缩小。
NOTE: Also, the scale of a pinch gesture goes from 0 to infinite with 0 to 1 being pinching in (zoom out) and 1 to infinite being pinching out (zoom in). To get a good zoom in zoom out effect with this you'd need to have a math equation. Velocity is actually from -infinite to infinite with 0 being the starting point.
注意:此外,捏合手势的比例从 0 到无限,其中 0 到 1 是捏合(缩小),1 到无限是捏合(放大)。为了获得良好的放大缩小效果,您需要有一个数学方程式。速度实际上是从-无限到无限,以0为起点。
EDIT: Fixed crash on range exception. Thanks to @garafajon!
编辑:修复了范围异常的崩溃。感谢@garafajon!
回答by CodaFi
回答by bcattle
Since iOS 7 you can set the zoom directly with the videoZoomFactor
property of AVCaptureDevice
.
由于iOS的7,您可以用直接设置变焦videoZoomFactor
的性能AVCaptureDevice
。
Tie the scale
property of the UIPinchGestureRecognizer
to thevideoZoomFactor
with a scaling constant. This will let you vary the sensitivity to taste:
领带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);
}
}
}
Note that AVCaptureDevice
, along everything else related to AVCaptureSession
, is not thread safe. So you probably don't want to do this from the main queue.
请注意,AVCaptureDevice
与 相关的所有其他内容都不AVCaptureSession
是线程安全的。因此,您可能不想从主队列中执行此操作。
回答by jnblanchard
Swift 4
Add a pinch gesture recognizer to the front-most view and connect it to this action (pinchToZoom). captureDeviceshould be the instance currently providing input to the capture session. pinchToZoomprovides smooth zooming for both front&backcapture devices.
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
}
}
It'll be useful to declare zoomFactor on your camera or vc. I usually put it on the same singleton that has AVCaptureSession. This will act as a default value for captureDevice's videoZoomFactor.
在您的相机或 vc 上声明 zoomFactor 会很有用。我通常把它放在有 AVCaptureSession 的同一个单例上。这将作为captureDevice的videoZoomFactor的默认值。
var zoomFactor: Float = 1.0
回答by Nicola Vacca
I started from the @Gabriel Cartier's solution (thanks). In my code I've preferred to use the smoother rampToVideoZoomFactor and a simpler way to compute the device's scale factor.
我从@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);
}
}
}
回答by DragonCherry
In swift version, you can zoom in/out by simply passing scaled number on videoZoomFactor. Following code in UIPinchGestureRecognizer handler will solve the issue.
在 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
}
In here, pivotPinchScale is a CGFloat property that declared in your controller somewhere.
在这里,pivotPinchScale 是一个 CGFloat 属性,它在您的控制器中的某处声明。
You may also refer to following project to see how camera works with UIPinchGestureRecognizer. https://github.com/DragonCherry/CameraPreviewController
您也可以参考以下项目来了解相机如何与 UIPinchGestureRecognizer 配合使用。 https://github.com/DragonCherry/CameraPreviewController
回答by side
based on @Gabriel Cartier 's answer :
基于@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);
}
}
}
}
回答by Anton K
There is an easier way to handle camera zoom level with pinch recognizer. The only thing you need to do is take cameraDevice.videoZoomFactor
and set it to the recognizer on .began
state like this
使用捏合识别器处理相机缩放级别有一种更简单的方法。您唯一需要做的就是将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
}
}
回答by Emanuel Martinez Vazquez
I am using iOS SDK 8.3 and the AVfoundation framework and for me using the following method worked for :
我正在使用 iOS SDK 8.3 和 AVfoundation 框架,对我来说,使用以下方法适用于:
nameOfAVCaptureVideoPreviewLayer.affineTransform = CGAffineTransformMakeScale(scaleX, scaleY)
For saving the picture with the same scale I used the following method:
为了以相同的比例保存图片,我使用了以下方法:
nameOfAVCaptureConnection.videoScaleAndCropFactor = factorNumber;
The code bellow is for getting the image in the scale
下面的代码用于获取比例尺中的图像
[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
if(imageDataSampleBuffer != NULL){
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
UIImage *image = [UIImage imageWithData:imageData];
}
}];