【问题标题】:CGImage to MPSTexture or MPSImageCGImage 到 MPSTexture 或 MPSImage
【发布时间】:2017-12-07 23:35:15
【问题描述】:

我有一个由 CVPixelbuffer (ARGB) 构成的 CGImage。我想将该 CGImage 转换为 MTLTexture。我用:

  let texture: MTLTexture = try m_textureLoader.newTexture(with: cgImage, options: [MTKTextureLoaderOptionSRGB : NSNumber(value: true)] )

稍后我想在具有 3 个通道的 MPSImage 中使用纹理:

let sid   = MPSImageDescriptor(channelFormat: MPSImageFeatureChannelFormat.float16, width: 40, height: 40, featureChannels: 3)
preImage    = MPSTemporaryImage(commandBuffer:  commandBuffer, imageDescriptor: sid)
lanczos.encode(commandBuffer: commandBuffer, sourceTexture: texture!, destinationTexture: preImage.texture)
scale.encode  (commandBuffer: commandBuffer, sourceImage: preImage, destinationImage: srcImage)

现在我的问题: textureLoader.newTexture(...) 如何将四个 ARGB 通道映射到 MPSImageDescriptor 中指定的 3 个通道? 如何确保使用 RGB 组件而不是例如精氨酸? 有没有办法指定通道映射?

谢谢,克里斯

【问题讨论】:

    标签: swift metal cgimage metalkit


    【解决方案1】:

    为什么不直接从 CVPixelBuffer 构造 MTLTexture 呢?更快!

    在程序开始时执行一次:

    // declare this somewhere, so we can re-use it
    var textureCache: CVMetalTextureCache?
    
    // create the texture cache object
    guard CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, device, nil, &textureCache) == kCVReturnSuccess else {
      print("Error: could not create a texture cache")
      return false
    }
    

    拥有 CVPixelBuffer 后执行此操作:

    let width = CVPixelBufferGetWidth(pixelBuffer)
    let height = CVPixelBufferGetHeight(pixelBuffer)
    
    var texture: CVMetalTexture?
    CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, textureCache,
          pixelBuffer, nil, .bgra8Unorm, width, height, 0, &texture)
    
    if let texture = texture {
      metalTexture = CVMetalTextureGetTexture(texture)
    }
    

    现在metalTexture 包含一个 MTLTexture 对象,其中包含 CVPixelBuffer 的内容。

    【讨论】:

    • 非常感谢。看起来轻松多了。但是,我只需要将像素缓冲区的一部分裁剪到纹理中。我知道 ROI 坐标。您的解决方案可以扩展到这样做吗?如果有怎么办?谢谢
    • 您可以创建一个额外的MTLTexture,其大小等于 ROI 的大小,然后使用 MTLBlitCommandEncoder 从您从像素缓冲区创建的纹理中复制该区域。这将暂时使用更多内存,但之后您可以丢弃或重用第一个纹理。使用 Core Image 或 Accelerate 有其他方法可以做到这一点,但据我所知,它们都需要额外的分配和副本。
    • 另一种不涉及复制的方法是将整个像素缓冲区转换为纹理,然后指定一个 clipRect 和偏移量到您正在调用的 MPS 内核(或者如果您使用的是 Lanczos规模,在其上设置一个变换)。这将使 MPS 内核在图像的较小部分上工作。 (但是,如果您的裁剪区域相对于源图像非常小,那么像@warrenm 建议的那样进行复制可能会更有效。)
    • 我的 ROI 大约是 40px...80px,而我的图像是 960x540,所以制作副本可能会更有效。但是(回到我原来的问题)我仍然在为频道排序而苦苦挣扎。我的 CNN 假设 RGB 排序,但我的 Pixelbuffer 似乎提供 BGRA 并使事情变得更加复杂,我从 Pixelbuffer -> CGImage -> Texture 开始,然后最迟我不知道实际的通道排序是什么......任何建议?如果我将灰度放入每个 RGG 缓冲区,我的 CNN 会产生有用的东西,如果我将颜色放入其中就会变得疯狂......
    • 您可以在屏幕上显示的CGImage,因此您可以看到那里的像素顺序(如果显示正常,则为RGB)。对于纹理,您可以打印出它的 pixelFormat 以查看通道顺序是什么。您还可以将纹理转换回 UIImage 并显示它。此处的示例代码:github.com/hollance/YOLO-CoreML-MPSNNGraph/blob/master/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-14
    • 2014-08-27
    • 2011-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多