如何在 Metal 中 crop/resize 纹理数组
How to crop/resize texture array in Metal
假设我有一个 N 通道 MPSImage
或基于 MTLTexture
的纹理数组。
如何从中裁剪一个区域,复制所有 N 个通道,但更改 "pixel size"?
我将只讨论裁剪案例,因为调整大小案例涉及重新采样并且稍微复杂一些。如果您真的需要它,请告诉我。
假设您的源 MPSImage
是 128x128 像素的 12 特征通道(3 切片)图像,您的目标图像是 64x64 像素的 8 特征通道图像(2 切片),并且您想要将源的最后两个切片的右下角 64x64 区域复制到目标中。
据我所知,没有 API 允许您一次复制 from/to 数组纹理的多个切片,因此您需要发出多个 blit 命令来覆盖所有切片:
let sourceRegion = MTLRegionMake3D(64, 64, 0, 64, 64, 1)
let destOrigin = MTLOrigin(x: 0, y: 0, z: 0)
let firstSlice = 1
let lastSlice = 2 // inclusive
let commandBuffer = commandQueue.makeCommandBuffer()
let blitEncoder = commandBuffer.makeBlitCommandEncoder()
for slice in firstSlice...lastSlice {
blitEncoder.copy(from: sourceImage.texture,
sourceSlice: slice,
sourceLevel: 0,
sourceOrigin: sourceRegion.origin,
sourceSize: sourceRegion.size,
to: destImage.texture,
destinationSlice: slice - firstSlice,
destinationLevel: 0,
destinationOrigin: destOrigin)
}
blitEncoder.endEncoding()
commandBuffer.commit()
我不确定您为什么要裁剪,但请记住,MPSCNN 层可以处理 MPSImage 的较小部分。只需设置 offset
和 clipRect
属性,图层将仅在源图像的该区域起作用。
事实上,您可以使用 MPSCNNNeuronLinear
以这种方式进行裁剪。不确定这是否比使用 blit 编码器更快或更慢,但它肯定更简单。
编辑: 添加了代码示例。这是凭记忆输入的,所以可能会有小错误,但这是总体思路:
// Declare this somewhere:
let linearNeuron = MPSCNNNeuronLinear(a: 1, b: 0)
然后当你运行你的神经网络时,添加以下内容:
let yourImage: MPSImage = ...
let commandBuffer = ...
// This describes the size of the cropped image.
let imgDesc = MPSImageDescriptor(...)
// If you're going to use the cropped image in other layers
// then it's a good idea to make it a temporary image.
let tempImg = MPSTemporaryImage(commandBuffer: commandBuffer, imageDescriptor: imgDesc)
// Set the cropping offset:
linearNeuron.offset = MPSOffset(x: ..., y: ..., z: 0)
// The clip rect is the size of the output image.
linearNeuron.clipRect = MTLRegionMake(0, 0, imgDesc.width, imgDesc.height)
linearNeuron.encode(commandBuffer: commandBuffer, sourceImage: yourImage, destinationImage: tempImg)
// Here do your other layers, taking tempImg as input.
. . .
commandBuffer.commit()
假设我有一个 N 通道 MPSImage
或基于 MTLTexture
的纹理数组。
如何从中裁剪一个区域,复制所有 N 个通道,但更改 "pixel size"?
我将只讨论裁剪案例,因为调整大小案例涉及重新采样并且稍微复杂一些。如果您真的需要它,请告诉我。
假设您的源 MPSImage
是 128x128 像素的 12 特征通道(3 切片)图像,您的目标图像是 64x64 像素的 8 特征通道图像(2 切片),并且您想要将源的最后两个切片的右下角 64x64 区域复制到目标中。
据我所知,没有 API 允许您一次复制 from/to 数组纹理的多个切片,因此您需要发出多个 blit 命令来覆盖所有切片:
let sourceRegion = MTLRegionMake3D(64, 64, 0, 64, 64, 1)
let destOrigin = MTLOrigin(x: 0, y: 0, z: 0)
let firstSlice = 1
let lastSlice = 2 // inclusive
let commandBuffer = commandQueue.makeCommandBuffer()
let blitEncoder = commandBuffer.makeBlitCommandEncoder()
for slice in firstSlice...lastSlice {
blitEncoder.copy(from: sourceImage.texture,
sourceSlice: slice,
sourceLevel: 0,
sourceOrigin: sourceRegion.origin,
sourceSize: sourceRegion.size,
to: destImage.texture,
destinationSlice: slice - firstSlice,
destinationLevel: 0,
destinationOrigin: destOrigin)
}
blitEncoder.endEncoding()
commandBuffer.commit()
我不确定您为什么要裁剪,但请记住,MPSCNN 层可以处理 MPSImage 的较小部分。只需设置 offset
和 clipRect
属性,图层将仅在源图像的该区域起作用。
事实上,您可以使用 MPSCNNNeuronLinear
以这种方式进行裁剪。不确定这是否比使用 blit 编码器更快或更慢,但它肯定更简单。
编辑: 添加了代码示例。这是凭记忆输入的,所以可能会有小错误,但这是总体思路:
// Declare this somewhere:
let linearNeuron = MPSCNNNeuronLinear(a: 1, b: 0)
然后当你运行你的神经网络时,添加以下内容:
let yourImage: MPSImage = ...
let commandBuffer = ...
// This describes the size of the cropped image.
let imgDesc = MPSImageDescriptor(...)
// If you're going to use the cropped image in other layers
// then it's a good idea to make it a temporary image.
let tempImg = MPSTemporaryImage(commandBuffer: commandBuffer, imageDescriptor: imgDesc)
// Set the cropping offset:
linearNeuron.offset = MPSOffset(x: ..., y: ..., z: 0)
// The clip rect is the size of the output image.
linearNeuron.clipRect = MTLRegionMake(0, 0, imgDesc.width, imgDesc.height)
linearNeuron.encode(commandBuffer: commandBuffer, sourceImage: yourImage, destinationImage: tempImg)
// Here do your other layers, taking tempImg as input.
. . .
commandBuffer.commit()