【问题标题】:How to crop an image from AVCapture to a rect seen on the display如何将图像从 AVCapture 裁剪为显示器上看到的矩形
【发布时间】:2013-04-11 14:42:40
【问题描述】:

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

我正在使用AVCaptureSessionAVCaptureVideoPreviewLayer 创建自己的相机界面。界面显示一个矩形。下面是填满整个屏幕的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;
}

有没有人有“正确的组合”方法来完成这项工作? :)

【问题讨论】:

    标签: ios avcapturesession


    【解决方案1】:

    在 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
    }
    

    【讨论】:

    • 什么是 previewLayer,出现错误“未解析的标识符”
    • 感谢您转换为 Swift 3。顺便说一句,您是否应该将 scale: 1.0 更改为 scale: originalImage.scale ?
    • @Yatendra 我知道我迟到了。但也许对于其他人来说也很有趣:previewLayer ist your AVCaptureVideoPreviewLayer
    【解决方案2】:

    我已经通过使用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 依赖图像。您可以删除方向信息以进行进一步的图像处理。

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

    【讨论】:

    • @unknownStack 我有同样的问题,但你的代码并没有解决我的问题。我将原点和拍摄图像保存在相册库中,但它们毕竟是一样的 :( 但是当在 uiimageView 中设置它时,它会显示一个非常缩放的图像 :(
    【解决方案3】:

    Swift 4 中:

    我更喜欢从不强制解包以避免崩溃,所以我在我的中使用了可选项和guards

    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
    }
    

    【讨论】:

      【解决方案4】:

      AVMakeRectWithAspectRatioInsideRect 此 api 来自 AVFoundation,它将返回给定裁剪大小的图像的裁剪区域。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多