我有一个使用AV Foundation的照片应用。我使用AVCaptureVideoPreviewLayer设置了一个预览图层,它占据了屏幕的上半部分。因此,当用户试图拍摄他们的照片时,他们只能看到屏幕上半部分看到的内容。
这很好用,但是当用户实际拍摄照片并尝试将照片设置为图层的内容时,图像会失真。我做了研究并意识到我需要裁剪图像。
我想做的就是裁剪完整拍摄的图像,这样剩下的就是用户最初在屏幕上半部分看到的内容。
我已经能够实现这一目标但我通过输入手动CGRect值来做到这一点,但它看起来仍然不完美。必须有一种更简单的方法来做到这一点。
在过去的两天里,关于裁剪图像,我已经完成了关于堆栈溢出的每一篇文章,并且没有任何工作。
必须有一种方法可以编程方式裁剪捕获的图像,以便最终图像与预览图层中最初看到的图像完全相同。
这是我的viewDidLoad实现:
- (void)viewDidLoad
{
[super viewDidLoad];
AVCaptureSession *session =[[AVCaptureSession alloc]init];
[session setSessionPreset:AVCaptureSessionPresetPhoto];
AVCaptureDevice *inputDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSError *error = [[NSError alloc]init];
AVCaptureDeviceInput *deviceInput = [AVCaptureDeviceInput deviceInputWithDevice:inputDevice error:&error];
if([session canAddInput:deviceInput])
[session addInput:deviceInput];
CALayer *rootLayer = [[self view]layer];
[rootLayer setMasksToBounds:YES];
_previewLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:session];
[_previewLayer setFrame:CGRectMake(0, 0, rootLayer.bounds.size.width, rootLayer.bounds.size.height/2)];
[_previewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
[rootLayer insertSublayer:_previewLayer atIndex:0];
_stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
[session addOutput:_stillImageOutput];
[session startRunning];
}
以下是用户按下按钮捕捉照片时运行的代码:
-(IBAction)stillImageCapture {
AVCaptureConnection *videoConnection = nil;
for (AVCaptureConnection *connection in _stillImageOutput.connections){
for (AVCaptureInputPort *port in [connection inputPorts]){
if ([[port mediaType] isEqual:AVMediaTypeVideo]){
videoConnection = connection;
break;
}
}
if (videoConnection) {
break;
}
}
NSLog(@"about to request a capture from: %@", _stillImageOutput);
[_stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
if(imageDataSampleBuffer) {
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
UIImage *image = [[UIImage alloc]initWithData:imageData];
CALayer *subLayer = [CALayer layer];
subLayer.frame = _previewLayer.frame;
image = [self rotate:image andOrientation:image.imageOrientation];
//Below is the crop that is sort of working for me, but as you can see I am manually entering in values and just guessing and it still does not look perfect.
CGRect cropRect = CGRectMake(0, 650, 3000, 2000);
CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], cropRect);
subLayer.contents = (id)[UIImage imageWithCGImage:imageRef].CGImage;
subLayer.frame = _previewLayer.frame;
[_previewLayer addSublayer:subLayer];
}
}];
}
答案 0 :(得分:19)
查看AVCaptureVideoPreviewLayer
s
-(CGRect)metadataOutputRectOfInterestForRect:(CGRect)layerRect
此方法可让您轻松将图层的可见CGRect转换为实际的相机输出。
有一点需要注意:物理相机没有安装在顶部和#34;而是顺时针旋转了90度。 (所以如果你拿着你的iPhone - Home Button,那么相机实际上是正面朝上的。)
记住这一点,您必须转换上述方法为您提供的CGRect,将图像裁剪为屏幕上的内容。
示例:
CGRect visibleLayerFrame = THE ACTUAL VISIBLE AREA IN THE LAYER FRAME
CGRect metaRect = [self.previewView.layer metadataOutputRectOfInterestForRect:visibleLayerFrame];
CGSize originalSize = [originalImage size];
if (UIInterfaceOrientationIsPortrait(_snapInterfaceOrientation)) {
// For portrait images, swap the size of the image, because
// here the output image is actually rotated relative to what you see on screen.
CGFloat temp = originalSize.width;
originalSize.width = originalSize.height;
originalSize.height = temp;
}
// metaRect is fractional, that's why we multiply here
CGRect cropRect;
cropRect.origin.x = metaRect.origin.x * originalSize.width;
cropRect.origin.y = metaRect.origin.y * originalSize.height;
cropRect.size.width = metaRect.size.width * originalSize.width;
cropRect.size.height = metaRect.size.height * originalSize.height;
cropRect = CGRectIntegral(cropRect);
这可能有点令人困惑,但让我真正理解的是:
按住您的设备" Home Button right" - >你会看到x轴实际上位于"高度"你的iPhone,而y轴位于"宽度"你的iPhone这就是肖像图像的原因,你必须换掉尺寸;)
答案 1 :(得分:8)
@Cabus有一个有效的解决方案,你应该对他的答案进行投票。但是,我使用以下内容在Swift中创建了自己的版本:
// The image returned in initialImageData will be larger than what
// is shown in the AVCaptureVideoPreviewLayer, so we need to crop it.
let image : UIImage = UIImage(data: initialImageData)!
let originalSize : CGSize
let visibleLayerFrame = self.previewView!.bounds // THE ACTUAL VISIBLE AREA IN THE LAYER FRAME
// Calculate the fractional size that is shown in the preview
let metaRect : CGRect = (self.videoPreviewLayer?.metadataOutputRectOfInterestForRect(visibleLayerFrame))!
if (image.imageOrientation == UIImageOrientation.Left || image.imageOrientation == UIImageOrientation.Right) {
// For these images (which are portrait), swap the size of the
// image, because here the output image is actually rotated
// relative to what you see on screen.
originalSize = CGSize(width: image.size.height, height: image.size.width)
}
else {
originalSize = image.size
}
// metaRect is fractional, that's why we multiply here.
let cropRect : CGRect = CGRectIntegral(
CGRect( x: metaRect.origin.x * originalSize.width,
y: metaRect.origin.y * originalSize.height,
width: metaRect.size.width * originalSize.width,
height: metaRect.size.height * originalSize.height))
let finalImage : UIImage =
UIImage(CGImage: CGImageCreateWithImageInRect(image.CGImage, cropRect)!,
scale:1,
orientation: image.imageOrientation )
答案 2 :(得分:1)
@Erik Allen在 Swift 3 中的回答:
let originalSize: CGSize
let visibleLayerFrame = self?.photoView.bounds
// Calculate the fractional size that is shown in the preview
let metaRect = (self?.videoPreviewLayer?.metadataOutputRectOfInterest(for: visibleLayerFrame ?? CGRect.zero)) ?? CGRect.zero
if (image.imageOrientation == UIImageOrientation.left || image.imageOrientation == UIImageOrientation.right) {
// For these images (which are portrait), swap the size of the
// image, because here the output image is actually rotated
// relative to what you see on screen.
originalSize = CGSize(width: image.size.height, height: image.size.width)
} else {
originalSize = image.size
}
let cropRect: CGRect = CGRect(x: metaRect.origin.x * originalSize.width, y: metaRect.origin.y * originalSize.height, width: metaRect.size.width * originalSize.width, height: metaRect.size.height * originalSize.height).integral
if let finalCgImage = image.cgImage?.cropping(to: cropRect) {
let finalImage = UIImage(cgImage: finalCgImage, scale: 1.0, orientation: image.imageOrientation)
// User your image...
}