如何从AVCapture中将图像裁剪到显示屏上显示的矩形

这让我疯狂,因为我无法工作。 我有以下情况:

我正在使用AVCaptureSessionAVCaptureVideoPreviewLayer来创build我自己的相机界面。 界面显示一个矩形。 下面是填充整个屏幕的AVCaptureVideoPreviewLayer

我希望以某种方式裁剪捕捉的图像,使得生成的图像精确显示在显示器上显示的内容。

我的设置看起来像这样:

 _session = [[AVCaptureSession alloc] init]; AVCaptureSession *session = _session; session.sessionPreset = AVCaptureSessionPresetPhoto; AVCaptureDevice *camera = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; if (camera == nil) { [self showImagePicker]; _isSetup = YES; return; } AVCaptureVideoPreviewLayer *captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session]; captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; captureVideoPreviewLayer.frame = self.liveCapturePlaceholderView.bounds; [self.liveCapturePlaceholderView.layer addSublayer:captureVideoPreviewLayer]; NSError *error; AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:camera error:&error]; if (error) { HGAlertViewWrapper *av = [[HGAlertViewWrapper alloc] initWithTitle:kFailedConnectingToCameraAlertViewTitle message:kFailedConnectingToCameraAlertViewMessage cancelButtonTitle:kFailedConnectingToCameraAlertViewCancelButtonTitle otherButtonTitles:@[kFailedConnectingToCameraAlertViewRetryButtonTitle]]; [av showWithBlock:^(NSString *buttonTitle){ if ([buttonTitle isEqualToString:kFailedConnectingToCameraAlertViewCancelButtonTitle]) { [self.delegate gloameCameraViewControllerDidCancel:self]; } else { [self setupAVSession]; } }]; } [session addInput:input]; NSDictionary *options = @{ AVVideoCodecKey : AVVideoCodecJPEG }; _stillImageOutput = [[AVCaptureStillImageOutput alloc] init]; [_stillImageOutput setOutputSettings:options]; [session addOutput:_stillImageOutput]; [session startRunning]; _isSetup = YES; 

我正在捕捉像这样的图像:

 [_stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error) { if (error) { MWLogDebug(@"Error capturing image from camera. %@, %@", error, [error userInfo]); _capturePreviewLayer.connection.enabled = YES; } else { NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer]; UIImage *image = [[UIImage alloc] initWithData:imageData]; CGRect cropRect = [self createCropRectForImage:image]; UIImage *croppedImage;// = [self cropImage:image toRect:cropRect]; UIGraphicsBeginImageContext(cropRect.size); [image drawAtPoint:CGPointMake(-cropRect.origin.x, -cropRect.origin.y)]; croppedImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); self.capturedImage = croppedImage; [_session stopRunning]; } }]; 

createCropRectForImage:方法中,我尝试了各种方法来计算切出图像的矩形,但目前还没有成功。

 - (CGRect)createCropRectForImage:(UIImage *)image { CGPoint maskTopLeftCorner = CGPointMake(self.maskRectView.frame.origin.x, self.maskRectView.frame.origin.y); CGPoint maskBottomRightCorner = CGPointMake(self.maskRectView.frame.origin.x + self.maskRectView.frame.size.width, self.maskRectView.frame.origin.y + self.maskRectView.frame.size.height); CGPoint maskTopLeftCornerInLayerCoords = [_capturePreviewLayer convertPoint:maskTopLeftCorner fromLayer:self.maskRectView.layer.superlayer]; CGPoint maskBottomRightCornerInLayerCoords = [_capturePreviewLayer convertPoint:maskBottomRightCorner fromLayer:self.maskRectView.layer.superlayer]; CGPoint maskTopLeftCornerInDeviceCoords = [_capturePreviewLayer captureDevicePointOfInterestForPoint:maskTopLeftCornerInLayerCoords]; CGPoint maskBottomRightCornerInDeviceCoords = [_capturePreviewLayer captureDevicePointOfInterestForPoint:maskBottomRightCornerInLayerCoords]; float x = maskTopLeftCornerInDeviceCoords.x * image.size.width; float y = (1 - maskTopLeftCornerInDeviceCoords.y) * image.size.height; float width = fabsf(maskTopLeftCornerInDeviceCoords.x - maskBottomRightCornerInDeviceCoords.x) * image.size.width; float height = fabsf(maskTopLeftCornerInDeviceCoords.y - maskBottomRightCornerInDeviceCoords.y) * image.size.height; return CGRectMake(x, y, width, height); } 

这是我目前的版本,但没有得到正确的比例。 有人可以帮助我!

我也尝试使用这种方法来裁剪我的形象:

 - (UIImage*)cropImage:(UIImage*)originalImage toRect:(CGRect)rect{ CGImageRef imageRef = CGImageCreateWithImageInRect([originalImage CGImage], rect); CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef); CGColorSpaceRef colorSpaceInfo = CGImageGetColorSpace(imageRef); CGContextRef bitmap = CGBitmapContextCreate(NULL, rect.size.width, rect.size.height, CGImageGetBitsPerComponent(imageRef), CGImageGetBytesPerRow(imageRef), colorSpaceInfo, bitmapInfo); if (originalImage.imageOrientation == UIImageOrientationLeft) { CGContextRotateCTM (bitmap, radians(90)); CGContextTranslateCTM (bitmap, 0, -rect.size.height); } else if (originalImage.imageOrientation == UIImageOrientationRight) { CGContextRotateCTM (bitmap, radians(-90)); CGContextTranslateCTM (bitmap, -rect.size.width, 0); } else if (originalImage.imageOrientation == UIImageOrientationUp) { // NOTHING } else if (originalImage.imageOrientation == UIImageOrientationDown) { CGContextTranslateCTM (bitmap, rect.size.width, rect.size.height); CGContextRotateCTM (bitmap, radians(-180.)); } CGContextDrawImage(bitmap, CGRectMake(0, 0, rect.size.width, rect.size.height), imageRef); CGImageRef ref = CGBitmapContextCreateImage(bitmap); UIImage *resultImage=[UIImage imageWithCGImage:ref]; CGImageRelease(imageRef); CGContextRelease(bitmap); CGImageRelease(ref); return resultImage; } 

有没有人有正确的方法来完成这项工作? 🙂

我已经通过使用metadataOutputRectOfInterestForRect函数解决了这个问题。

它适用于任何方向。

 [_stillImageOutput captureStillImageAsynchronouslyFromConnection:stillImageConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) { if (error) { [_delegate cameraView:self error:@"Take picture failed"]; } else { NSData *jpegData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer]; UIImage *takenImage = [UIImage imageWithData:jpegData]; CGRect outputRect = [_previewLayer metadataOutputRectOfInterestForRect:_previewLayer.bounds]; CGImageRef takenCGImage = takenImage.CGImage; size_t width = CGImageGetWidth(takenCGImage); size_t height = CGImageGetHeight(takenCGImage); CGRect cropRect = CGRectMake(outputRect.origin.x * width, outputRect.origin.y * height, outputRect.size.width * width, outputRect.size.height * height); CGImageRef cropCGImage = CGImageCreateWithImageInRect(takenCGImage, cropRect); takenImage = [UIImage imageWithCGImage:cropCGImage scale:1 orientation:takenImage.imageOrientation]; CGImageRelease(cropCGImage); } } ]; 

takenImage仍然是imageOrientation相关的图像。 您可以删除方向信息进一步image processing。

 UIGraphicsBeginImageContext(takenImage.size); [takenImage drawAtPoint:CGPointZero]; takenImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); 

在Swift 3:

 private func cropToPreviewLayer(originalImage: UIImage) -> UIImage { let outputRect = previewLayer.metadataOutputRectOfInterest(for: previewLayer.bounds) var cgImage = originalImage.cgImage! let width = CGFloat(cgImage.width) let height = CGFloat(cgImage.height) let cropRect = CGRect(x: outputRect.origin.x * width, y: outputRect.origin.y * height, width: outputRect.size.width * width, height: outputRect.size.height * height) cgImage = cgImage.cropping(to: cropRect)! let croppedUIImage = UIImage(cgImage: cgImage, scale: 1.0, orientation: originalImage.imageOrientation) return croppedUIImage } 

AVMakeRectWithAspectRatioInsideRect这个API来自AVFoundation,它会返回给定裁剪大小的图像的裁剪区域。

看看这个库,它可以帮助你实现你想要的东西:)