【发布时间】:2012-04-19 02:42:54
【问题描述】:
我运行了一个简单的 AVCaptureSession,以在我的应用中获取相机源并拍照。如何使用相机的UIGestureRecognizer 实现“捏缩放”功能?
【问题讨论】:
-
你在哪里添加捏合手势?
标签: iphone objective-c ios avcapturesession avcapture
我运行了一个简单的 AVCaptureSession,以在我的应用中获取相机源并拍照。如何使用相机的UIGestureRecognizer 实现“捏缩放”功能?
【问题讨论】:
标签: iphone objective-c ios avcapturesession avcapture
接受的答案实际上已经过时,我不确定它是否真的会拍摄放大图像的照片。有一种方法可以像 bcattle 回答说的那样放大。他的答案的问题在于,它不负责用户可以放大然后从该缩放位置重新启动的事实。他的解决方案会产生一些不太优雅的跳跃。
最简单、最优雅的方法是使用捏合手势的速度。
-(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 到无限表示捏合(放大)。要获得良好的放大缩小效果,您需要有一个数学方程式。速度实际上是从-infinite到infinite,以0为起点。
编辑:修复了范围异常时的崩溃问题。感谢@garafajon!
【讨论】:
许多人尝试通过将图层上的变换属性设置为CGAffineTransformMakeScale(gesture.scale.x, gesture.scale.y); 来做到这一点
请参阅 here 了解捏合缩放的完整实现。
【讨论】:
从 iOS 7 开始,您可以直接使用 AVCaptureDevice 的 videoZoomFactor 属性设置缩放。
将UIPinchGestureRecognizer 的scale 属性与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 相关的所有其他内容都不是线程安全的。因此,您可能不想从主队列中执行此操作。
【讨论】:
斯威夫特 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
}
}
在您的相机或 vc 上声明 zoomFactor 会很有用。我通常把它放在有 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);
}
}
}
【讨论】:
有一种更简单的方法可以使用捏合识别器来处理相机缩放级别。您唯一需要做的就是将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
}
}
【讨论】:
基于@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);
}
}
}
}
【讨论】:
我正在使用 iOS SDK 8.3 和 AVfoundation 框架,对我来说,使用以下方法适用于:
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];
}
}];
【讨论】: