【问题标题】:How to convert a UIImage to a CVPixelBuffer [duplicate]如何将 UIImage 转换为 CVPixelBuffer [重复]
【发布时间】:2017-11-11 17:24:12
【问题描述】:

Apple 的新 CoreML 框架有一个预测函数,它接受 CVPixelBuffer。为了对UIImage 进行分类,必须在两者之间进行转换。

我从 Apple 工程师那里得到的转换代码:

1  // image has been defined earlier
2
3     var pixelbuffer: CVPixelBuffer? = nil
4   
5     CVPixelBufferCreate(kCFAllocatorDefault, Int(image.size.width), Int(image.size.height), kCVPixelFormatType_OneComponent8, nil, &pixelbuffer)
6     CVPixelBufferLockBaseAddress(pixelbuffer!, CVPixelBufferLockFlags(rawValue:0))
7   
8     let colorspace = CGColorSpaceCreateDeviceGray()
9     let bitmapContext = CGContext(data: CVPixelBufferGetBaseAddress(pixelbuffer!), width: Int(image.size.width), height: Int(image.size.height), bitsPerComponent: 8, bytesPerRow: CVPixelBufferGetBytesPerRow(pixelbuffer!), space: colorspace, bitmapInfo: 0)!
10  
11    bitmapContext.draw(image.cgImage!, in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))

此解决方案速度很快,适用于灰度图像。必须根据图像类型进行的更改是:

  • 第 5 行 | kCVPixelFormatType_OneComponent8 到另一个 OSTypekCVPixelFormatType_32ARGB 用于 RGB)
  • 第 8 行 | colorSpace 到另一个 CGColorSpaceCGColorSpaceCreateDeviceRGB 用于 RGB)
  • 第 9 行 | bitsPerComponent 为内存每个像素的位数(RGB 为 32)
  • 第 9 行 | bitmapInfo 到非零 CGBitmapInfo 属性(kCGBitmapByteOrderDefault 是默认值)

【问题讨论】:

  • 我认为CVPixelBuffer只是相机图像的约定,还有ciImagecgImage可用
  • 为什么不使用视觉框架?您可以 (1) 初始化 VNCoreMLModel 对象,然后使用完成处理程序初始化 (2) VNCoreMLRequest,然后使用 (3) VNImageRequestHandler - 它可以采用 CIImage 或文件 URL。结果是VNClassificationObservation 对象的集合。当然,现在,如果您想将 UIImage 转换为 CVPixelBuffer - 您真的没有在任何地方您的问题中没有问题:-) - 你可以简单地在互联网上搜索并找到这个:gist.github.com/cieslak/743f9321834c5a40597afa1634a48343
  • @dfd 是的,所以我确实想将 UIImage 转换为 CVPixelBuffer 以使用 CoreML 模型,但我请 WWDC 的 Apple 工程师解决了这个问题上面的代码。鉴于会议上的其他几个人也遇到了同样的问题,我想我会分享一个比 github 解决方案更简单地实现其目的的解决方案。不过感谢您的建议!
  • @Ryan:您是在问问题还是想分享解决方案?在后一种情况下,您应该将其发布为一个自我回答的问题:stackoverflow.com/help/self-answer
  • +1 给 Martin R,请将问题的后半部分移到问题的答案中,让其他人也能回答!

标签: swift uiimage cvpixelbuffer coreml


【解决方案1】:

你可以看看这个教程https://www.hackingwithswift.com/whats-new-in-ios-11,代码在 Swift 4 中

func buffer(from image: UIImage) -> CVPixelBuffer? {
  let attrs = [kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue, kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue] as CFDictionary
  var pixelBuffer : CVPixelBuffer?
  let status = CVPixelBufferCreate(kCFAllocatorDefault, Int(image.size.width), Int(image.size.height), kCVPixelFormatType_32ARGB, attrs, &pixelBuffer)
  guard (status == kCVReturnSuccess) else {
    return nil
  }

  CVPixelBufferLockBaseAddress(pixelBuffer!, CVPixelBufferLockFlags(rawValue: 0))
  let pixelData = CVPixelBufferGetBaseAddress(pixelBuffer!)

  let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
  let context = CGContext(data: pixelData, width: Int(image.size.width), height: Int(image.size.height), bitsPerComponent: 8, bytesPerRow: CVPixelBufferGetBytesPerRow(pixelBuffer!), space: rgbColorSpace, bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue)

  context?.translateBy(x: 0, y: image.size.height)
  context?.scaleBy(x: 1.0, y: -1.0)

  UIGraphicsPushContext(context!)
  image.draw(in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))
  UIGraphicsPopContext()
  CVPixelBufferUnlockBaseAddress(pixelBuffer!, CVPixelBufferLockFlags(rawValue: 0))

  return pixelBuffer
}

【讨论】:

  • 你知道为什么需要 translateBy 和 ScaleBY 吗?
  • 哦。从 UIKit 和 CoreGraphics 使用的不同坐标系移动。 Y 在 UIKit 中指向下方,在 CoreGraphics 中指向上方??
  • P.S.如果你使用这个缓冲区来做一些金属工作,那么将kCVPixelBufferMetalCompatibilityKey: kCFBooleanTrue 添加到attrs
  • 与 UIImage 的扩展相同的答案:
  • 像魅力一样工作!
猜你喜欢
  • 2017-07-13
  • 2022-12-21
  • 2020-06-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-16
  • 2015-02-21
  • 1970-01-01
相关资源
最近更新 更多