我正在尝试使用金属拉普拉斯算子从 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("")
}
Run Code Online (Sandbox Code Playgroud)
我怀疑问题出在 makeTexture 上:
let lapTex = self.device.makeTexture(descriptor: desc)
Run Code Online (Sandbox Code Playgroud)
看起来顺序或初始化是错误的,但找不到什么。
有谁知道出了什么问题?
谢谢
这里有一些错误。
首先,正如我在评论中提到的,命令缓冲区没有被提交,所以内核工作永远不会被执行。
其次,在尝试读回结果之前,您需要等待工作完成。(在 macOS 上,您还需要使用 blit 命令编码器来确保纹理的内容被复制回 CPU 可访问的内存。)
第三,使用适当的使用标志创建目标纹理很重要。.shaderRead在这种情况下,默认值是不够的,因为 MPS 内核写入纹理。因此,您应该显式设置usage纹理描述符上的属性(设置为[.shaderRead, .shaderWrite]或.shaderWrite,取决于您继续使用纹理的方式)。
第四,源纹理的像素格式可能不是可写格式,因此除非您绝对确定它是,否则请考虑将目标像素格式设置为已知可写格式(如.rgba8unorm),而不是假设目标应与源匹配。这在稍后创建CGImages时也有帮助。
最后,不能保证cgImagea的属性CIImage不是从 a 创建的非零CGImage。调用该属性不会(必然)创建一个新的 backing CGImage。因此,您需要以CGImage某种方式显式地创建一个。
这样做的一种方法是创建一个 Metal 设备支持CIContext并使用它的createCGImage(_:from:)方法。尽管这可能有效,但如果意图只是CGImage从 a创建a MTLTexture(例如出于显示目的),则似乎是多余的。
相反,请考虑使用该getBytes(_:bytesPerRow:from:mipmapLevel:)方法从纹理中获取字节并将它们加载到 CG 位图上下文中。然后CGImage从上下文创建 a是微不足道的。
这是一个计算图像拉普拉斯算子并返回结果图像的函数:
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()
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
428 次 |
| 最近记录: |