如何使用 Metal API 或 Accelerate Framework 绘制裁剪的位图?

Posted

技术标签:

【中文标题】如何使用 Metal API 或 Accelerate Framework 绘制裁剪的位图?【英文标题】:How to draw cropped bitmap using the Metal API or Accelerate Framework? 【发布时间】:2015-04-23 02:57:38 【问题描述】:

我正在实现一个裁剪视频帧的自定义视频合成器。目前我使用 Core Graphics 来做到这一点:

-(void)renderImage:(CGImageRef)image inBuffer:(CVPixelBufferRef)destination 
    CGRect cropRect = // some rect ...
    CGImageRef currentRectImage = CGImageCreateWithImageInRect(photoFullImage, cropRect);

    size_t width = CVPixelBufferGetWidth(destination);
    size_t height = CVPixelBufferGetHeight(destination);

    CGContextRef context = CGBitmapContextCreate(CVPixelBufferGetBaseAddress(destination),       // data
                                             width,
                                             height,
                                             8,                                              // bpp
                                             CVPixelBufferGetBytesPerRow(destination),
                                             CGImageGetColorSpace(backImage),
                                             CGImageGetBitmapInfo(backImage));

    CGRect frame = CGRectMake(0, 0, width, height);
    CGContextDrawImage(context, frame, currentRectImage);
    CGContextRelease(context);

如何使用 Metal API 来执行此操作?它应该快得多,对吧? 使用 Accelerate 框架(特别是 vImage)怎么样?那会更简单吗?

【问题讨论】:

【参考方案1】:

好的,我不知道这对您是否有用,但仍然如此。 查看以下代码:

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection

  CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);

  id<MTLTexture> textureY = nil;

  
    size_t width = CVPixelBufferGetWidth(pixelBuffer);
    size_t height = CVPixelBufferGetHeight(pixelBuffer);

    MTLPixelFormat pixelFormat = MTLPixelFormatBGRA8Unorm;

    CVMetalTextureRef texture = NULL;
    CVReturn status = CVMetalTextureCacheCreateTextureFromImage(NULL, _textureCache, pixelBuffer, NULL, pixelFormat, width, height, 0, &texture);
    if(status == kCVReturnSuccess)
    
      textureY = CVMetalTextureGetTexture(texture);
      if (self.delegate)
        [self.delegate textureUpdated:textureY];
      
      CFRelease(texture);
    
  

我使用此代码将CVPixelBufferRef 转换为MTLTexture。之后你可能应该创建blitCommandEncoder 并使用它的

func copyFromTexture(sourceTexture: MTLTexture, sourceSlice: Int, sourceLevel: Int, sourceOrigin: MTLOrigin, sourceSize: MTLSize, toTexture destinationTexture: MTLTexture, destinationSlice: Int, destinationLevel: Int, destinationOrigin: MTLOrigin)

在其中,您可以选择裁剪后的矩形并将其复制到其他纹理。

下一步是将生成的MTLTextures 转换回CVPixelBufferRefs 然后制作视频,不幸的是我不知道该怎么做。

真的很想听听你的想法。干杯。

【讨论】:

你可以很容易地从 CVMetalTextureRef 到 CGImageRef 再到 CVPixelBufferRef,但如果你需要实时性能,我在同一个泡菜中(试图弄清楚如何获得 CVPixelBufferRef)【参考方案2】:

因为它使用裸指针和未封装的数据,所以 vImage 将通过将指针移到图像的左上角以指向新的左上角并相应地减小高度和宽度来“裁剪”事物。您现在有一个 vImage_Buffer ,它引用图像中间的一个区域。当然,您仍然需要将内容再次导出为文件或将其复制到注定要绘制到屏幕上的内容。参见例如 vImageCreateCGImageFromBuffer()。

CG 可以通过 CGImageCreateWithImageInRect() 自己完成此操作

Metal 可以通过简单的计算复制内核、MTLBlitCommandEncoder blit 或将纹理 3D 渲染应用到具有适当坐标偏移的三角形集合来执行此操作。

【讨论】:

以上是关于如何使用 Metal API 或 Accelerate Framework 绘制裁剪的位图?的主要内容,如果未能解决你的问题,请参考以下文章

Xcode 日志记录:“启用金属 API 验证”

Metal API随着iOS 10与macOS 10.12新引入的Tessellation特性

何时在 Apple macOS 上使用 Metal 而不是 Accelerate API

使用Metal API与Imageblocks模板“隐式实例化”错误

是否有可能直接将 C++ 与 Metal API 一起使用

是否可以在 iPad Swift Playground 中使用 Metal 着色器代码?