给 ComputeShader class 增加以下三个方法:
// 从 OpenCV Mat 创建(只读的) Metal Texture
- (id<MTLTexture>) textureFromCVMat:(const void*)cv_data Width:(int) w Height:(int)h {
// Create a Metal texture descriptor
MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR8Unorm
width:w
height:h
mipmapped:NO];
textureDescriptor.storageMode = MTLStorageModeShared;
// Create a Metal texture from the descriptor
id<MTLTexture> texture = [_device newTextureWithDescriptor:textureDescriptor];
// Copy the data from the cv::Mat to the Metal texture
[texture replaceRegion:MTLRegionMake2D(0, 0, w, h) mipmapLevel:0 withBytes:cv_data bytesPerRow:w];
return texture;
}
// 从宽高创建空的(可写的)Metal Texture
- (id<MTLTexture>) writableTextureWithWidth:(int) w Height:(int)h {
// Create a Metal texture descriptor
MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR8Unorm
width:w
height:h
mipmapped:NO];
textureDescriptor.storageMode = MTLStorageModeShared;
textureDescriptor.usage = MTLTextureUsageShaderWrite;
// Create a Metal texture from the descriptor
id<MTLTexture> texture = [_device newTextureWithDescriptor:textureDescriptor];
return texture;
}
// 以 src Texture 为输入,进行 Sobel 计算,输出到 dst Texture
-(void) sobel:(id<MTLTexture>)src To:(id<MTLTexture>)dst
{
id<MTLCommandBuffer> commandBuffer_sobel = [_commandQueue commandBuffer];
assert(commandBuffer_sobel != nil);
self.sobelKernel=[[MPSImageSobel alloc] initWithDevice:_device];
[_sobelKernel encodeToCommandBuffer:commandBuffer_sobel sourceTexture:src destinationTexture:dst];
[commandBuffer_sobel commit];
[commandBuffer_sobel waitUntilCompleted];
}使用方法(在 NLE::pca_smooth_detection() 中):
id<MTLTexture> src_texture = [shader textureFromCVMat:img.data Width:col Height:row];
id<MTLTexture> dst_texture = [shader writableTextureWithWidth:col Height:row];
[shader sobel:src_texture To:dst_texture];运行效果:

关于 Metal Texture 数据读写,可以参考 Resource Objects: Buffers and Textures