0

我需要使用 MTKView 在 HDR10 ( kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange / BT2020 PQ 颜色空间) 中显示 CVPixelBuffers。我有以下代码要显示,但它会产生失真的图像,我无法确定它是显示 10 位颜色还是钳位颜色。另外无论是 bgra10_xr 还是 bgra10_xr_srgb 都应该是 colorPixelFormat 的正确选择以及两者的区别。

class CIImageView: MTKView {

    var image: CIImage? {
       didSet {
          self.draw()
      }
     }

   var originalImageExtent: CGRect = CGRect.zero 

   var scale: CGFloat {
       return max(self.frame.width / originalImageExtent.width, self.frame.height / originalImageExtent.height)
   } 


   func update() {
      guard let img = image, destRect.size.width <=    img.extent.size.width && destRect.size.height <=   img.extent.size.height else {
          return
       }
    
      self.draw()
   }

let context: CIContext
let commandQueue: MTLCommandQueue

convenience init(frame: CGRect) {
    let device = MetalCamera.metalDevice
    self.init(frame: frame, device: device)
    colorPixelFormat = .bgra10_xr_srgb
}

override init(frame frameRect: CGRect, device: MTLDevice?) {
    guard let device = device else {
        fatalError("Can't use Metal")
    }
    
    
    guard let cmdQueue = device.makeCommandQueue(maxCommandBufferCount: 5) else {
        fatalError("Can't make Command Queue")
    }
    
    commandQueue = cmdQueue
    
    context = CIContext(mtlDevice: device, options: [CIContextOption.cacheIntermediates: false])
    
    super.init(frame: frameRect, device: device)
    
    self.framebufferOnly = false
    self.enableSetNeedsDisplay = false
    self.isPaused = true
    self.clearColor = MTLClearColor(red: 0, green: 0, blue: 0, alpha: 0)
}

required init(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

override func draw(_ rect: CGRect) {
    
    guard let image = self.image else {
        return
    }
    
    let dRect = destRect
    
    let drawImage: CIImage
    
    if dRect == image.extent {
        drawImage = image
    } else {
        let scale = max(dRect.height / image.extent.height, dRect.width / image.extent.width)
        drawImage = image.transformed(by: CGAffineTransform(scaleX: scale, y: scale))
    }
    
    let commandBuffer = commandQueue.makeCommandBufferWithUnretainedReferences()
    guard let texture = self.currentDrawable?.texture else {
        return
    }
    
    let colorSpace = drawImage.colorSpace ?? CGColorSpaceCreateDeviceRGB()
    
    context.render(drawImage, to: texture, commandBuffer: commandBuffer, bounds: dRect, colorSpace: colorSpace)
    
    commandBuffer?.present(self.currentDrawable!)
    commandBuffer?.commit()
}

private var destRect: CGRect {
    
    let scale: CGFloat
    if UIScreen.main.scale == 3 {
        // BUG?
        scale = 2.0 * (2.0 / UIScreen.main.scale) * 2
    } else {
        scale = UIScreen.main.scale
    }
    let destRect = self.bounds.applying(CGAffineTransform(scaleX: scale, y: scale))
    
    return destRect
 }


func displayPixelBuffer(_ pixelBuffer: CVPixelBuffer, flip:FlipDirection) {
    self.image = CIImage(cvImageBuffer: pixelBuffer)
}

编辑:我尝试了手动设置 colorSpace 的建议,但颜色被洗掉了。

在此处输入图像描述

这是 iPhone 上使用 AVCaptureVideoPreviewLayer 的同一场景的屏幕截图。

在此处输入图像描述

4

1 回答 1

0

请在苹果论坛上查看此框架开发人员的回答:

CAMetalLayer.wantsExtendedDynamicRangeContent 仅在 macOS 上可用。Metal 没有公开在 iOS 设备上显示 HDR 内容的方法。HDR 只能用于视频和图像。

通过将 CAMetalLayer.pixelFormat 设置为 XR 格式(如 MTLPixelFormatBGR10_XR_sRGB),Metal 确实提供了一种方法来获得一些额外的空间,以获得比标准 sRGB 更好的色调映射 HDR 效果。虽然这不是真正的 HDR,如 HDR10 或杜比视界,但它确实改善了效果。


另外无论是 bgra10_xr 还是 bgra10_xr_srgb 都应该是 colorPixelFormat 的正确选择以及两者的区别。

要显示真正的HDR内容,您应该使用MTLPixelFormatRGBA16Float像素格式。

于 2021-10-08T21:24:34.577 回答