【问题标题】:How to get a UIImage from CMSampleBuffer using AVCaptureSession如何使用 AVCaptureSession 从 CMSampleBuffer 获取 UIImage
【发布时间】:2012-11-15 12:18:09
【问题描述】:

我一直在尝试在 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构造函数的数值?
  • 使用 iPhone 5 时,pixelBuffer 的值如下: width: 720 height: 1280 bytes per row: 1084
  • 您正在尝试为具有不同格式的 CVPixelBuffer 创建一个 CGBitmapContext。每行 1084 字节和 720 的宽度给出了每像素超过 1.5 字节。那不是RGB24,大概是平面格式,那么CVPixelBuffer的像素格式是什么?
  • 你是对的。打印 PixelFormatType 显示 CV420YpCbCr8BiPlanarVideoRange。所以看起来我需要转换为非平面 RGB。
  • 我不完全确定最好的方法,但我可能可以做到。如果你输入这个作为答案,我很乐意接受。

标签: ios image video xamarin.ios camera


【解决方案1】:

这个错误信息解释了它:

<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 中输出。
  • 对于像我这样的任何其他 AVFoundation 菜鸟,此错误可能只是因为您的相机捕获分辨率与您的 kCVPixelBufferWidthKey 和 kCVPixelBufferHeightKeys 不匹配
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多