【问题标题】:Cropping AVCaptureVideoPreviewLayer output to a square将 AVCaptureVideoPreviewLayer 输出裁剪为正方形
【发布时间】:2015-05-03 13:57:17
【问题描述】:

在抓取可视屏幕的裁剪 UIImage 时,我的 AVCaptureVideoPreviewLayer 方法出现问题。目前它正在工作,但没有输出我需要的正确作物。

我正在尝试输出一个正方形,但它(从外观上看)似乎给出了完整的高度并压缩了图像。

前图显示实时屏幕,后图显示按下捕获按钮后的图像。您可以看到它已垂直更改以适合正方形,但高度尚未垂直裁剪。

捕获图像代码

[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error) {

    if (imageSampleBuffer != NULL) {

        NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
        [self processImage:[UIImage imageWithData:imageData]];

    }
}];

裁剪代码

- (void) processImage:(UIImage *)image { //process captured image, crop, resize and rotate

        haveImage = YES;

    CGRect deviceScreen = _previewLayer.bounds;
    CGFloat width = deviceScreen.size.width;
    CGFloat height = deviceScreen.size.height;

    NSLog(@"WIDTH %f", width); // Outputing 320
    NSLog(@"HEIGHT %f", height); // Outputting 320

    UIGraphicsBeginImageContext(CGSizeMake(width, width));
    [image drawInRect: CGRectMake(0, 0, width, width)];

    UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    CGRect cropRect = CGRectMake(0, 0, width, width);

    CGImageRef imageRef = CGImageCreateWithImageInRect([smallImage CGImage], cropRect);

    CGImageRelease(imageRef);

    [captureImageGrab setImage:[UIImage imageWithCGImage:imageRef]];

}

【问题讨论】:

  • 你能把图片尺寸记录在processImage里面吗?还有什么是captureImageGrab?是UIImageView吗?

标签: ios objective-c avcapturesession


【解决方案1】:

你试过设置videoGravity吗?

previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill

【讨论】:

    【解决方案2】:

    即使预览层是方形的,请记住生成的静止图像保持其原始大小。

    据我所见,问题出在:

    UIGraphicsBeginImageContext(CGSizeMake(width, width));
    [image drawInRect: CGRectMake(0, 0, width, width)];
    

    您已经将上下文与第一行对齐。您仍然需要以原始格式绘制图像,它将被该上下文剪切。在第二行,您强制将原始图像绘制成正方形,从而使其看起来“被挤压”。

    您应该找到合适的图像高度,以保持原始比例,同时适合您的“宽度”。接下来,您将要在正方形上下文中以正确的大小(保持原始比例)绘制该图像。如果要裁剪中心,请更改绘图的 Y 位置。

    类似的东西:

    - (void) processImage:(UIImage *)image {
        UIGraphicsBeginImageContext(CGSizeMake(width, width));
        CGFloat imageHeight = floorf(width / image.width * image.height);
        CGFloat offsetY = floorf((imageHeight - width) / 2.0f);
        [image drawInRect: CGRectMake(0, -offsetY, width, imageHeight)];
        UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        [captureImageGrab setImage:smallImage];
    }
    

    应该可以的。

    【讨论】:

    • @memyselfandmyiphone 你有时间尝试吗,或者你已经解决了你的问题?
    【解决方案3】:

    查看代码在我看来,您正在将图像绘制成正方形。这是在不考虑纵横比的情况下缩小图像的部分。

    相反,请执行以下操作:

    - (void) processImage:(UIImage *)image { //process captured image, crop, resize and rotate
    
        haveImage = YES;
    
        CGRect deviceScreen = _previewLayer.bounds;
        CGFloat width = deviceScreen.size.width;
        CGFloat height = deviceScreen.size.height;
    
        NSLog(@"WIDTH %f", width); // Outputing 320
        NSLog(@"HEIGHT %f", height); // Outputting 320
    
        CGRect cropRect = CGRectZero;
        if (image.size.width > image.size.height) {
            cropRect.origin.x = (image.size.width-image.size.height)/2;
            cropRect.size = CGSizeMake(image.size.height, image.size.height);
        } else {
            cropRect.origin.y = (image.size.height-image.size.width)/2;
            cropRect.size = CGSizeMake(image.size.width, image.size.width);
        }
        CGImageRef croppedImage = CGImageCreateWithImageInRect(image.CGImage, cropRect);
    
        UIGraphicsBeginImageContext(CGSizeMake(width, width));
        [[UIImage imageWithCGImage:croppedImage] drawInRect:CGRectMake(0, 0, width, width)];
        UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    
        CGImageRelease(croppedImage);
    
        [captureImageGrab setImage:[UIImage imageWithCGImage:smallImage.CGImage scale:image.scale orientation:image.imageOrientation]];
    }
    

    【讨论】:

    • @memyselfandmyiphone 哦,是的,确实如此。查看更新的代码(我替换了 CGContextDrawImage 调用)。
    • 还在翻来覆去。看起来它还在凝结。 ://
    • @memyselfandmyiphone 我再次编辑了代码,检查最后一行。这应该可以确定方向。
    • 这固定了方向,但仍会导致垂直压缩图像
    • 对可能出现的问题有什么想法吗?
    【解决方案4】:

    由于相机拍摄的图像不是正方形,因此纵横比失真。将其绘制成正方形时,它将以扭曲的纵横比绘制。一种可能的解决方案是将图像绘制到保持纵横比的正方形中,例如:

    - (void) processImage:(UIImage *)image { //process captured image, crop, resize and rotate
    
        haveImage = YES;
    
        CGRect deviceScreen = _previewLayer.bounds;
        CGFloat width = deviceScreen.size.width;
        CGFloat height = deviceScreen.size.height;
    
        NSLog(@"WIDTH %f", width); // Outputing 320
        NSLog(@"HEIGHT %f", height); // Outputting 320
    
        UIGraphicsBeginImageContext(CGSizeMake(width, width));
    
        CGFloat aspect_h = image.size.height * width / image.size.width;
    
        [image drawInRect: CGRectMake(0, -(aspect_h - width)/2.0, width, aspect_h)];
    
        UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    
        CGRect cropRect = CGRectMake(0, 0, width, width);
    
        CGImageRef imageRef = CGImageCreateWithImageInRect([smallImage CGImage], cropRect);
    
        CGImageRelease(imageRef);
    
    }
    

    新图像的宽度为 width。对于该宽度,将保持纵横比的高度为 aspect_h。然后,图像沿 y 轴移动以使其垂直居中。

    请注意,如果您的应用要在非纵向的方向上工作,则需要做更多的工作。

    另一个观察:如果你使用 image.size.width 作为新图像上下文的宽度,你可以获得更好分辨率的图像。

    希望这会有所帮助!

    【讨论】:

      【解决方案5】:

      我的 previewLayer 是方形的,这对我没有任何帮助:

      previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
      previewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
      

      @Rufel's answer 是有效的!!!这是在 Swift 5 中

      func processImageIntoSquare(image: UIImage) -> UIImage? {
          
          guard let previewLayer = previewLayer else { return nil }
          
          let width = previewLayer.bounds.size.width
          
          let size = CGSize(width: width, height: width)
          UIGraphicsBeginImageContext(size)
          
          let imageHeight = floor(width / image.size.width * image.size.height)
          let offSetY = floor((imageHeight - width) / 2.0)
          
          let rect = CGRect(x: 0, y: -offSetY, width: width, height: imageHeight)
          image.draw(in: rect)
          
          let smallImage = UIGraphicsGetImageFromCurrentImageContext()
          UIGraphicsEndImageContext()
          
          return smallImage
      }
      

      这是我的使用方法:

      MyCameraController: AVCapturePhotoCaptureDelegate {
          
          func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
      
              if let error = error { return }
      
              guard let imageData = photo.fileDataRepresentation(), let imageFromCamera = UIImage(data: imageData) else { return }
      
              guard let squareImage = processImageIntoSquare(image: imageFromCamera) else { return }
      
              DispatchQueue.main.async { [weak self] in
      
                  self?.myImageView.image = squareImage
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2011-05-23
        • 2011-05-30
        • 2017-06-13
        • 2017-11-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-23
        • 2020-02-06
        相关资源
        最近更新 更多