How to crop an image from AVCapture to a rect seen on the display
In Swift 3:
private func cropToPreviewLayer(originalImage: UIImage) -> UIImage { let outputRect = previewLayer.metadataOutputRectConverted(fromLayerRect: 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}
I've solved this problem by using metadataOutputRectOfInterestForRect
function.
It works with any orientation.
[_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); } } ];
The takenImage is still imageOrientation
dependent image. You can delete orientation information for further image processing.
UIGraphicsBeginImageContext(takenImage.size);[takenImage drawAtPoint:CGPointZero];takenImage = UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();
In Swift 4:
I prefer to never force-unwrap to avoid crashes, so I use optionals and guards
in mine.
private func cropToPreviewLayer(originalImage: UIImage) -> UIImage? { guard let cgImage = originalImage.cgImage else { return nil } let outputRect = previewLayer.metadataOutputRectConverted(fromLayerRect: previewLayer.bounds) 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) if let croppedCGImage = cgImage.cropping(to: cropRect) { return UIImage(cgImage: croppedCGImage, scale: 1.0, orientation: originalImage.imageOrientation) } return nil}