如何使用AVCaptureSession从CMSampleBuffer获取UIImage

3

我一直在尝试使用MonoTouch进行实时视频图像处理。我正在使用AVCaptureSession从摄像头获取帧,它可以与AVCaptureVideoPreviewLayer配合使用。

我还成功地在我的委托类中获取了回调方法“DidOutputSampleBuffer”。然而,我尝试过的所有方法都无法从CMSampleBuffer中创建UIImage。

这是我设置捕获会话的代码:

captureSession = new AVCaptureSession ();
            captureSession.BeginConfiguration ();
            videoCamera = AVCaptureDevice.DefaultDeviceWithMediaType (AVMediaType.Video);

            if (videoCamera != null)
            {
                captureSession.SessionPreset = AVCaptureSession.Preset1280x720;

                videoInput = AVCaptureDeviceInput.FromDevice (videoCamera);

                if (videoInput != null)
                    captureSession.AddInput (videoInput);

                //DispatchQueue queue = new DispatchQueue ("videoFrameQueue");

                videoCapDelegate = new videoOutputDelegate (this);

                DispatchQueue queue = new DispatchQueue("videoFrameQueue");
                videoOutput = new AVCaptureVideoDataOutput ();

                videoOutput.SetSampleBufferDelegateAndQueue (videoCapDelegate, queue);
                videoOutput.AlwaysDiscardsLateVideoFrames = true;
                videoOutput.VideoSettings.PixelFormat = CVPixelFormatType.CV24RGB;

                captureSession.AddOutput (videoOutput);

                videoOutput.ConnectionFromMediaType(AVMediaType.Video).VideoOrientation = AVCaptureVideoOrientation.Portrait;

                previewLayer = AVCaptureVideoPreviewLayer.FromSession (captureSession);
                previewLayer.Frame = UIScreen.MainScreen.Bounds;
                previewLayer.AffineTransform = CGAffineTransform.MakeRotation (Convert.DegToRad (-90));
                //this.View.Layer.AddSublayer (previewLayer);

                captureSession.CommitConfiguration ();
                captureSession.StartRunning ();
            }

我尝试从样本缓冲区的图像缓冲区中创建一个CVPixelBuffer,然后将其转换为CGBitmapContext,方法如下:

public override void DidOutputSampleBuffer (AVCaptureOutput captureOutput, MonoTouch.CoreMedia.CMSampleBuffer sampleBuffer, AVCaptureConnection connection)
    {

        CVPixelBuffer pixelBuffer = sampleBuffer.GetImageBuffer () as CVPixelBuffer;
        CVReturn flag = pixelBuffer.Lock (0);
        if(flag == CVReturn.Success)
        {
            CGBitmapContext context = new CGBitmapContext
                    (
                        pixelBuffer.BaseAddress,
                        pixelBuffer.Width,
                        pixelBuffer.Height,
                        8,
                        pixelBuffer.BytesPerRow, 
                        CGColorSpace.CreateDeviceRGB (), 
                        CGImageAlphaInfo.PremultipliedFirst
                        );

            UIImage image = new UIImage(context.ToImage());

            ProcessImage (image);

            pixelBuffer.Unlock(0);

        }else
            Debug.Print(flag.ToString()

        sampleBuffer.Dispose();
    }

这会导致以下错误。
<Error>: CGBitmapContextCreate: invalid data bytes/row: should be at least 2880 for 8 integer bits/component, 3 components, kCGImageAlphaPremultipliedFirst.

即使对一些参数进行了微调,我仍然会遇到无效的句柄异常或者在原生Objective-C中发生段错误。
我还尝试过简单地使用CVImageBuffer创建CIImage,并从中创建UIImage,如下所示:
public override void DidOutputSampleBuffer (AVCaptureOutput captureOutput, MonoTouch.CoreMedia.CMSampleBuffer sampleBuffer, AVCaptureConnection connection)
    {

        CIImage cImage = new CIImage(sampleBuffer.GetImageBuffer ());
        UIImage image = new UIImage(cImage);
        ProcessImage (image);

        sampleBuffer.Dispose();
    }

当初始化CIImage时出现异常:

NSInvalidArgumentException Reason: -[CIImage initWithCVImageBuffer:]: unrecognized selector sent to instance 0xc821d0

这感觉像是MonoTouch的某种错误,但如果我遗漏了什么或者只是以一种奇怪的方式尝试做这个,请告诉我一些替代方案。谢谢!

调用CGBitmapContext构造函数时,确切的数值是什么? - Rolf Bjarne Kvinge
使用 iPhone 5,pixelBuffer 具有以下值:宽度:720 高度:1280 每行字节数:1084 - Ben Holmes
你正在尝试为具有不同格式的CVPixelBuffer创建CGBitmapContext。每行1084字节,宽度为720,每像素略高于1.5字节。这不是RGB24,可能是平面格式,那么CVPixelBuffer的像素格式是什么? - Rolf Bjarne Kvinge
你是正确的。打印PixelFormatType会显示CV420YpCbCr8BiPlanarVideoRange。因此,看起来我需要转换为非平面RGB。 - Ben Holmes
我不是完全确定最好的方法,但我可能可以处理。如果您将此作为答案输入,我将很乐意接受它。 - Ben Holmes
1个回答

3
这个错误信息是这样解释的:
<Error>: CGBitmapContextCreate: invalid data bytes/row: should be at least 2880 for 8 integer bits/component, 3 components, kCGImageAlphaPremultipliedFirst.

该图像的宽度为720像素,每行的字节数为1084,每个像素略高于1.5个字节 - 这并不是RGB24格式(每个像素有3个字节),而是一些平面格式。

您可能需要检查AVCaptureVideoDataOutput.AvailableVideoCVPixelFormatTypes以获取可用的像素格式,以查看是否有任何支持的格式更容易使用。


似乎即使我设置了一个包含在AvailableVideoCVPixelFormatTypes中的像素格式类型,它仍然输出CV420YpCbCr8BiPlanarVideoRange。 - Ben Holmes
对于像我这样的其他AVFoundation新手,这个错误可能只是因为您的相机捕获分辨率与kCVPixelBufferWidthKey和kCVPixelBufferHeightKeys不匹配。 - user3344977

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接