【问题标题】:Is it possible to render AVCaptureVideoPreviewLayer in a graphics context?是否可以在图形上下文中渲染 AVCaptureVideoPreviewLayer?
【发布时间】:2012-02-19 05:00:33
【问题描述】:

这似乎是一个简单的任务,但它让我发疯。 是否可以将包含 AVCaptureVideoPreviewLayer 作为子图层的 UIView 转换为要保存的图像? 我想创建一个增强现实叠加层并有一个按钮将图片保存到相机胶卷。按住电源按钮 + 主页键将屏幕截图捕获到相机胶卷,这意味着我所有的捕获逻辑都在工作,并且任务是可能的。但我似乎无法使其以编程方式工作。

我正在使用 AVCaptureVideoPreviewLayer 捕获相机图像的实时预览。我所有渲染图像的尝试都失败了:

  previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession];
//start the session, etc...


//this saves a white screen
- (IBAction)saveOverlay:(id)sender {
    NSLog(@"saveOverlay");

    UIGraphicsBeginImageContext(appDelegate.window.bounds.size);
        UIGraphicsBeginImageContext(scrollView.frame.size);

    [previewLayer.presentationLayer renderInContext:UIGraphicsGetCurrentContext()];


//    [appDelegate.window.layer renderInContext:UIGraphicsGetCurrentContext()];

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

    UIImageWriteToSavedPhotosAlbum(screenshot, self, 
                                   @selector(image:didFinishSavingWithError:contextInfo:), nil);
}

//这会渲染所有内容,除了预览层,它是空白的。

[appDelegate.window.layer renderInContext:UIGraphicsGetCurrentContext()];

我在某处读到这可能是由于 iPhone 的安全问题。这是真的吗?

明确一点:我不想为相机保存图像。我想保存叠加在另一个图像上的透明预览层,从而创建透明度。但由于某种原因,我无法使其工作。

【问题讨论】:

    标签: iphone objective-c uiview avfoundation


    【解决方案1】:

    我喜欢@Roma 关于使用 GPU Image 的建议——好主意。 . . .但是,如果你想要一个纯粹的 CocoaTouch 方法,可以这样做:

    实现 AVCaptureVideoDataOutputSampleBufferDelegate

    - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
    fromConnection:(AVCaptureConnection *)connection
    {
        // Create a UIImage+Orientation from the sample buffer data
        if (_captureFrame)
        {
            [captureSession stopRunning];
    
            _captureFrame = NO;
            UIImage *image = [ImageTools imageFromSampleBuffer:sampleBuffer];
            image = [image rotate:UIImageOrientationRight];
    
            _frameCaptured = YES;
    
            if (delegate != nil)
            {
                [delegate cameraPictureTaken:image];
            }
        }
    }
    

    如下截图:

    + (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer 
    {
        // Get a CMSampleBuffer's Core Video image buffer for the media data
        CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 
        // Lock the base address of the pixel buffer
        CVPixelBufferLockBaseAddress(imageBuffer, 0); 
    
        // Get the number of bytes per row for the pixel buffer
        void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer); 
    
        // Get the number of bytes per row for the pixel buffer
        size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); 
        // Get the pixel buffer width and height
        size_t width = CVPixelBufferGetWidth(imageBuffer); 
        size_t height = CVPixelBufferGetHeight(imageBuffer); 
    
        // Create a device-dependent RGB color space
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
    
        // Create a bitmap graphics context with the sample buffer data
        CGContextRef context = CGBitmapContextCreate(baseAddress, width, height, 8, 
                                                 bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); 
        // Create a Quartz image from the pixel data in the bitmap graphics context
        CGImageRef quartzImage = CGBitmapContextCreateImage(context); 
        // Unlock the pixel buffer
        CVPixelBufferUnlockBaseAddress(imageBuffer,0);
    
        // Free up the context and color space
        CGContextRelease(context); 
        CGColorSpaceRelease(colorSpace);
    
        // Create an image object from the Quartz image
        UIImage *image = [UIImage imageWithCGImage:quartzImage];
    
        // Release the Quartz image
        CGImageRelease(quartzImage);
    
        return (image);
    }
    

    将 UIImage 与叠加层混合

    • 现在您有了 UIImage,将其添加到新的 UIView。
    • 在顶部添加叠加层作为子视图。

    捕获新的 UIView

    + (UIImage*)imageWithView:(UIView*)view
    {
        UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, [UIScreen    mainScreen].scale);
        [view.layer renderInContext:UIGraphicsGetCurrentContext()];
        UIImage* img = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return img;
    }
    

    【讨论】:

    • 我想这就是我要找的。将尝试让它运行并让你知道它是如何进行的。感谢您的快速回答!
    • 你是否在使用 uiimage 扩展来表示 image = [image rotate:UIImageOrientationRight]; ?
    • 是的,该步骤是可选的。不过,让我给你代码。
    • Jasper,非常感谢你,你的回答和 cmets 对它的工作很有帮助!
    【解决方案2】:

    我可以建议你试试 GPU Image。

    https://github.com/BradLarson/GPUImage

    它使用openGL,所以速度相当快。它可以处理来自相机的图片并为它们添加过滤器(有很多),包括边缘检测、运动检测等等

    它类似于 OpenCV,但根据我自己的经验,GPU 图像更容易与您的项目连接,并且语言是 Objective-c。

    如果您决定将 box2d 用于物理,则可能会出现问题 - 它也使用 openGl,您需要花费一些时间直到这 2 个框架停止战斗))

    【讨论】:

    • 我猜我和 OP 正在尝试使用 avcapturevideopreviewlayer 获取实时视频源,并尝试添加缩放图像。我正在尝试基于人脸检测在实时视频提要上添加缩放图像,保存的屏幕截图只是叠加层的截图,而不是 avcapturevideopreviewlayer。
    • 在 GPU 图像中,以前在图层上添加过滤器,所以我很确定有办法只保存当前图层(GPU 图像中有方法从帧制作 UIImage)如果你想得到显示屏上有什么 - 有办法以编程方式制作屏幕截图。我现在不记得了,但我在 stackoverflow 上看到过)
    • 罗马,非常感谢您的回答。虽然我没有使用 GPUImage,但我肯定很快就会使用它。感谢您将其作为替代选项发布。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-05
    • 1970-01-01
    • 1970-01-01
    • 2023-02-07
    • 2021-07-26
    相关资源
    最近更新 更多