将 CMSampleBuffer 转换为 UIImage

Posted

技术标签:

【中文标题】将 CMSampleBuffer 转换为 UIImage【英文标题】:Convert CMSampleBuffer to UIImage 【发布时间】:2017-03-24 10:45:37 【问题描述】:

我正在尝试将 sampleBuffer 转换为 UIImage 并使用 colorspaceGray 在图像视图中显示它。但它显示为下图。我认为转换存在问题。如何转换 CMSampleBuffer?

func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) 
    print("buffered")
    let imageBuffer: CVImageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!
    CVPixelBufferLockBaseAddress(imageBuffer, CVPixelBufferLockFlags(rawValue: 0))
    let width: Int = CVPixelBufferGetWidth(imageBuffer)
    let height: Int = CVPixelBufferGetHeight(imageBuffer)
    let bytesPerRow: Int = CVPixelBufferGetBytesPerRow(imageBuffer)
    let lumaBuffer = CVPixelBufferGetBaseAddress(imageBuffer)

    //let planeCount : Int = CVPixelBufferGetPlaneCount(imageBuffer)
    let grayColorSpace: CGColorSpace = CGColorSpaceCreateDeviceGray()
    let context: CGContext = CGContext(data: lumaBuffer, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow , space: grayColorSpace, bitmapInfo: CGImageAlphaInfo.none.rawValue)!
    let dstImageFilter: CGImage = context.makeImage()!
    let imageRect : CGRect = CGRect(x: 0, y: 0, width: width, height: height)
    context.draw(dstImageFilter, in: imageRect)
    let image = UIImage(cgImage: dstImageFilter)
    DispatchQueue.main.sync(execute: () -> Void in
        self.imageTest.image = image
    )

【问题讨论】:

【参考方案1】:

转换很简单:

func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) 
     let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!
     let ciimage = CIImage(cvPixelBuffer: imageBuffer)   
     let image = self.convert(cmage: ciimage) 


// Convert CIImage to UIImage
func convert(cmage: CIImage) -> UIImage 
     let context = CIContext(options: nil)
     let cgImage = context.createCGImage(cmage, from: cmage.extent)!
     let image = UIImage(cgImage: cgImage)
     return image

【讨论】:

谢谢,还有如何将 UIImage 转换回 CMSampleBuffer? 完美解决方案!!谢谢 这太棒了!谢谢你。我唯一的问题是图像的方向。它始终默认为横向。你碰巧知道为什么会这样以及如何解决这个问题 @mikey 到目前为止我记得是我在得到它之后旋转了图像,或者你可以改变图像的方向。 嗨@mumu 我在获得图像后最终旋转它。谢谢你的回答!【参考方案2】:

现在可以通过UIImage 上的新便捷方法进一步改进上述解决方案。我在下面概述了一个带有图像方向校正的现代解决方案。该解决方案不使用CGImage 转换,从而提高了运行时性能。

func orientation() -> UIImage.Orientation 
    let curDeviceOrientation = UIDevice.current.orientation
    var exifOrientation: UIImage.Orientation
    switch curDeviceOrientation 
        case UIDeviceOrientation.portraitUpsideDown:  // Device oriented vertically, Home button on the top
            exifOrientation = .left
        case UIDeviceOrientation.landscapeLeft:       // Device oriented horizontally, Home button on the right
            exifOrientation = .upMirrored
        case UIDeviceOrientation.landscapeRight:      // Device oriented horizontally, Home button on the left
            exifOrientation = .down
        case UIDeviceOrientation.portrait:            // Device oriented vertically, Home button on the bottom
            exifOrientation = .up
        default:
            exifOrientation = .up
    
    return exifOrientation


func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) 
    guard let imageBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else  return 
    let ciimage = CIImage(cvPixelBuffer: imageBuffer)
    let image = UIImage(ciImage: ciimage, scale: 1.0, orientation: orientation())

【讨论】:

【参考方案3】:

看起来CMSampleBuffer 正在为您提供 RGBA 数据,然后您可以从中直接构建灰度图像。您将需要构建一个新缓冲区,在其中为每个像素执行类似gray = (pixel.red+pixel.green+pixel.blue)/3 的操作。或者您需要将收到的数据创建一个普通的 RGBA 图像,然后将其转换为灰度。

但是在您的代码中,您根本没有进行转换。您使用CVPixelBufferGetBaseAddress 将原始指针指向缓冲区,而不管那里有什么类型的数据。然后你只需在创建图像时传递相同的指针,假设接收到的数据是灰度的。

【讨论】:

我觉得也可以指定抓包的输出格式,让抓包输出的格式符合预期。 如果数据已经是灰度的,那么为什么要出现这些条带?为什么不只是普通的灰色图像? @Matic 我在此之前尝试了第一种方法。但是它在从上下文制作图像时崩溃并给出了这个错误:copy read only: vm_copy failed: status 1 @RubaiyatJahanMumu “如果数据已经是灰度的,那么为什么这些条带会出现?”:很难预测结果,但它可能与使用 alpha 通道(始终为 255)有关作为颜色成分。 “它在制作图像时崩溃了”:您可能有溢出。您需要仔细检查收到的缓冲区并匹配您使用它们生成图像的方式。也许缓冲区仍然是RGB。或者,也许您正在使用其他类型的缓冲区。告诉我你的 bytesPerRow 和 width 的结果是什么。还有什么是 bitsPerComponent。 @RubaiyatJahanMumu 我将从尝试创建普通图像开始。然后尝试尝试如何创建它的灰度版本。

以上是关于将 CMSampleBuffer 转换为 UIImage的主要内容,如果未能解决你的问题,请参考以下文章

将 AudioBufferList 转换为 CMSampleBuffer 会产生意外结果

使用准确的 CMTime 将 AudioBuffer 转换为 CMSampleBuffer

如何在 Swift 中将 CMSampleBuffer 转换为数据?

使用 ReplayKit 广播时将 CMSampleBuffer 转换为 .mov

使用 CMSampleBuffer 生成 Float32 数组(Float32 PCM 数据)

如何将 Int16 音频样本的数据转换为浮点音频样本数组