Ios 用Apple Metal MPSImageLaplacian生成Laplacian图像

Ios 用Apple Metal MPSImageLaplacian生成Laplacian图像,ios,swift,metal,Ios,Swift,Metal,我正在尝试使用金属拉普拉斯算子从rgb CGImage生成拉普拉斯图像 使用的当前代码: if let croppedImage = self.cropImage2(image: UIImage(ciImage: image), rect: rect)?.cgImage { let commandBuffer = self.commandQueue.makeCommandBuffer()! let laplacian = MPSImageLaplacian(device: self.

我正在尝试使用金属拉普拉斯算子从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)
  • 调试器中lapTex的宽度和高度无效,尽管desc和srcTex包含有效数据,包括宽度和高度
看起来顺序或初始化错误,但找不到什么

有人知道怎么回事吗


谢谢

这里有一些地方不对劲

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

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

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

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

CGImage
s

最后,不能保证
CIImage
cgImage
属性在不是从
cgImage
创建时是非零的。调用属性并不(一定)创建新的备份
CGImage
。因此,您需要以某种方式显式创建
CGImage

一种方法是创建一个支持
CIContext
的金属设备,并使用其
createCGImage(u:from:)
方法。虽然这可能会起作用,但如果只是想从
MTLTexture
创建
CGImage
(比方说,为了显示目的),这似乎是多余的

取而代之的是,考虑使用<代码> GETBACK({:ByTeSePro::MIPMAPVELVE:)/Cult>方法从纹理中获取字节,并将它们加载到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()
}

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