【问题标题】:Generate Laplacian image by Apple-Metal MPSImageLaplacian通过 Apple-Metal MPSImageLaplacian 生成拉普拉斯图像
【发布时间】:2019-07-23 14:21:24
【问题描述】:

我正在尝试使用金属拉普拉斯算子从 rgb CGImage 生成拉普拉斯算子图像。

当前使用的代码:

if let croppedImage = self.cropImage2(image: UIImage(ciImage: image), rect: rect)?.cgImage {

  let commandBuffer = self.commandQueue.makeCommandBuffer()!

  let laplacian = MPSImageLaplacian(device: self.device)

  let textureLoader = MTKTextureLoader(device: self.device)

  let options: [MTKTextureLoader.Option : Any]? = nil

  let srcTex = try! textureLoader.newTexture(cgImage: croppedImage, options: options)

  let desc = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: srcTex.pixelFormat, width: srcTex.width, height: srcTex.height, mipmapped: false)

  let lapTex = self.device.makeTexture(descriptor: desc)

  laplacian.encode(commandBuffer: commandBuffer, sourceTexture: srcTex, destinationTexture: lapTex!)

  let output = CIImage(mtlTexture: lapTex!, options: [:])?.cgImage

  print("output: \(output?.width)")


  print("") 
}

我怀疑问题出在 makeTexture:

  let lapTex = self.device.makeTexture(descriptor: desc)
  • 虽然 desc 和 srcTex 包含包括宽度和高度在内的有效数据,但调试器中 lapTex 的宽度和高度无效。

看起来顺序或初始化有误,但找不到。

有人知道出了什么问题吗?

谢谢

【问题讨论】:

  • 您似乎没有在命令缓冲区上调用commit(),这意味着内核永远不会执行。此外,为了将生成的图像内容作为CGImage 获取,您需要确保首先执行这些命令finish。您可以通过在提交后在命令缓冲区上调用waitUntilCompleted() 来做到这一点。请注意,这将是一个阻塞操作,您应该尽可能避免在主线程上执行此操作。
  • 谢谢,这在代码中确实缺少,现在在添加它并调用之后:let output = CIImage(mtlTexture: lapTex!, options: [:])?.cgImage, 仍然得到 nil。你知道如何将其转换为有效的 CIImage 吗?

标签: ios swift metal


【解决方案1】:

这里有一些问题。

首先,正如我在评论中提到的,命令缓冲区没有被提交,所以内核工作永远不会被执行。

其次,您需要等待工作完成,然后再尝试回读结果。 (在 macOS 上,您还需要使用 blit 命令编码器来确保将纹理的内容复制回 CPU 可访问的内存。)

第三,使用适当的使用标志创建目标纹理很重要。在这种情况下,.shaderRead 的默认值是不够的,因为 MPS 内核会写入纹理。因此,您应该在纹理描述符上显式设置usage 属性(设置为[.shaderRead, .shaderWrite].shaderWrite,具体取决于您继续使用纹理的方式)。

第四,源纹理的像素格式可能不是可写格式,因此除非您绝对确定它是,否则请考虑将目标像素格式设置为已知可写格式(如@ 987654325@) 而不是假设目标应该与源匹配。这也有助于以后创建CGImages。

最后,不能保证CIImagecgImage 属性不是从CGImage 创建的非零。调用该属性不会(必然)创建新的支持CGImage。因此,您需要以某种方式显式创建CGImage

这样做的一种方法是创建一个 Metal 设备支持的 CIContext 并使用其 createCGImage(_:from:) 方法。虽然这可能可行,但如果意图只是从 MTLTexture 创建一个 CGImage 似乎是多余的(例如,出于显示目的)。

相反,考虑使用getBytes(_:bytesPerRow:from:mipmapLevel:) 方法从纹理中获取字节并将它们加载到CG 位图上下文中。然后从上下文创建CGImage 就很简单了。

这是一个计算图像的拉普拉斯算子并返回结果图像的函数:

func laplacian(_ image: CGImage) -> CGImage? {
    let commandBuffer = self.commandQueue.makeCommandBuffer()!

    let laplacian = MPSImageLaplacian(device: self.device)

    let textureLoader = MTKTextureLoader(device: self.device)
    let options: [MTKTextureLoader.Option : Any]? = nil
    let srcTex = try! textureLoader.newTexture(cgImage: image, options: options)

    let desc = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: srcTex.pixelFormat,
                                                        width: srcTex.width,
                                                        height: srcTex.height,
                                                        mipmapped: false)
    desc.pixelFormat = .rgba8Unorm
    desc.usage = [.shaderRead, .shaderWrite]

    let lapTex = self.device.makeTexture(descriptor: desc)!

    laplacian.encode(commandBuffer: commandBuffer, sourceTexture: srcTex, destinationTexture: lapTex)

    #if os(macOS)
    let blitCommandEncoder = commandBuffer.makeBlitCommandEncoder()!
    blitCommandEncoder.synchronize(resource: lapTex)
    blitCommandEncoder.endEncoding()
    #endif

    commandBuffer.commit()
    commandBuffer.waitUntilCompleted()

    // Note: You may want to use a different color space depending
    // on what you're doing with the image
    let colorSpace = CGColorSpaceCreateDeviceRGB()
    // Note: We skip the last component (A) since the Laplacian of the alpha
    // channel of an opaque image is 0 everywhere, and that interacts oddly
    // when we treat the result as an RGBA image.
    let bitmapInfo = CGImageAlphaInfo.noneSkipLast.rawValue
    let bytesPerRow = lapTex.width * 4
    let bitmapContext = CGContext(data: nil,
                                  width: lapTex.width,
                                  height: lapTex.height,
                                  bitsPerComponent: 8,
                                  bytesPerRow: bytesPerRow,
                                  space: colorSpace,
                                  bitmapInfo: bitmapInfo)!
    lapTex.getBytes(bitmapContext.data!,
                    bytesPerRow: bytesPerRow,
                    from: MTLRegionMake2D(0, 0, lapTex.width, lapTex.height),
                    mipmapLevel: 0)
    return bitmapContext.makeImage()
}

【讨论】:

    猜你喜欢
    • 2016-08-09
    • 2014-07-16
    • 2021-03-20
    • 1970-01-01
    • 2018-11-20
    • 2017-01-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多