ios – ARKit/SpriteKit – 将pixelBufferAttributes设置为SKVideoNode或在视频中制作透明像素(色度键效果)另一种方式

我的目标是使用ARKit在真实环境中呈现2D动画角色.动画角色是视频的一部分,在视频的以下快照中显示

使用代码完全显示视频本身没有任何问题:

func view(_ view: ARSKView,nodeFor anchor: aranchor) -> SKNode? {
    guard let urlString = Bundle.main.path(forResource: "resourceName",ofType: "mp4") else { return nil }

    let url = URL(fileURLWithPath: urlString)
    let asset = AVAsset(url: url)
    let item = AVPlayerItem(asset: asset)
    let player = AVPlayer(playerItem: item)

    let videoNode = SKVideoNode(avPlayer: player)
    videoNode.size = CGSize(width: 200.0,height: 150.0)
    videoNode.anchorPoint = CGPoint(x: 0.5,y: 0.0)

    return videoNode
}

代码的结果显示在以下应用程序的屏幕截图中,如预期:

但正如你所看到的,角色的背景并不是很好,所以我需要让它消失,以便创造出实际上站在水平面上的角色的幻觉.
我试图通过对视频产生色度键效果来实现这一点.

>对于那些不熟悉色度键的人来说,这是有时在电视上看到的“绿屏效果”的名称,以使颜色透明.

我对色度键效果的处理方法是创建基于“CIColorCube”CIFilter的自定义滤镜,然后使用AVVideoComposition将滤镜应用于视频.

首先,是创建过滤器的代码

func RGBtoHSV(r : Float,g : Float,b : Float) -> (h : Float,s : Float,v : Float) {
    var h : CGFloat = 0
    var s : CGFloat = 0
    var v : CGFloat = 0
    let col = UIColor(red: CGFloat(r),green: CGFloat(g),blue: CGFloat(b),alpha: 1.0)
    col.getHue(&h,saturation: &s,brightness: &v,alpha: nil)
    return (Float(h),Float(s),Float(v))
}

func colorCubeFilterForChromakey(hueAngle: Float) -> CIFilter {

    let hueRange: Float = 20 // degrees size pie shape that we want to replace
    let minHueAngle: Float = (hueAngle - hueRange/2.0) / 360
    let maxHueAngle: Float = (hueAngle + hueRange/2.0) / 360

    let size = 64
    var cubedata = [Float](repeating: 0,count: size * size * size * 4)
    var rgb: [Float] = [0,0]
    var hsv: (h : Float,v : Float)
    var offset = 0

    for z in 0 ..< size {
        rgb[2] = Float(z) / Float(size) // blue value
        for y in 0 ..< size {
            rgb[1] = Float(y) / Float(size) // green value
            for x in 0 ..< size {

                rgb[0] = Float(x) / Float(size) // red value
                hsv = RGBtoHSV(r: rgb[0],g: rgb[1],b: rgb[2])
                // Todo: Check if hsv.s > 0.5 is really nesseccary
                let alpha: Float = (hsv.h > minHueAngle && hsv.h < maxHueAngle && hsv.s > 0.5) ? 0 : 1.0

                cubedata[offset] = rgb[0] * alpha
                cubedata[offset + 1] = rgb[1] * alpha
                cubedata[offset + 2] = rgb[2] * alpha
                cubedata[offset + 3] = alpha
                offset += 4
            }
        }
    }
    let b = cubedata.withUnsafeBufferPointer { Data(buffer: $0) }
    let data = b as NSData

    let colorCube = CIFilter(name: "CIColorCube",withInputParameters: [
        "inputCubedimension": size,"inputCubedata": data
        ])
    return colorCube!
}

然后通过修改函数func视图(_ view:ARSKView,nodeFor anchor:aranchor)将视频应用于视频的代码 – > SKNode?我之前写的:

func view(_ view: ARSKView,ofType: "mp4") else { return nil }

    let url = URL(fileURLWithPath: urlString)
    let asset = AVAsset(url: url)

    let filter = colorCubeFilterForChromakey(hueAngle: 38)
    let composition = AVVideoComposition(asset: asset,applyingCIFiltersWithHandler: { request in
        let source = request.sourceImage
        filter.setValue(source,forKey: kCIInputimageKey)
        let output = filter.outputimage

        request.finish(with: output!,context: nil)
    })

    let item = AVPlayerItem(asset: asset)
    item.videoComposition = composition
    let player = AVPlayer(playerItem: item)

    let videoNode = SKVideoNode(avPlayer: player)
    videoNode.size = CGSize(width: 200.0,y: 0.0)

    return videoNode
}

如果像素颜色与背景的色调范围匹配,则代码应该将视频的每个帧的所有像素替换为alpha = 0.0.
但是我没有获得透明像素,而是将这些像素设置为黑色,如下图所示:

现在,虽然这不是想要的效果,但它并不让我感到惊讶,因为我知道这是iOS用alpha通道显示视频的方式.
但这是真正的问题 – 当在AVPlayer中显示普通视频时,可以选择将AVPlayerLayer添加到视图中,并将pixelBufferAttributes设置为它,让玩家层知道我们使用透明像素缓冲区,就像这样:

let playerLayer = AVPlayerLayer(player: player)
playerLayer.bounds = view.bounds
playerLayer.position = view.center
playerLayer.pixelBufferAttributes = [(kCVPixelBufferPixelFormatTypeKey as String): kCVPixelFormatType_32BGRA]
view.layer.addSublayer(playerLayer)

这段代码为我们提供了一个透明背景(GOOD!)但固定大小和位置(不好……)的视频,如您在此屏幕截图中所示:

我想达到相同的效果,但是在SKVideoNode上,而不是在AVPlayerLayer上.但是,我找不到任何方法将pixelBufferAttributes设置为SKVideoNode,并且设置播放器层无法实现ARKit的预期效果,因为它已固定在位.

我的问题是否有任何解决方案,或者是否有其他技术可以达到相同的预期效果

解决方法

解决方案非常简单!
所有需要做的是将视频添加为SKEffectNode的子节点,并将过滤器应用于SKEffectNode而不是视频本身(不需要AVVideoComposition).
这是我使用的代码
func view(_ view: ARSKView,nodeFor anchor: aranchor) -> SKNode? {
    // Create and configure a node for the anchor added to the view's session.
    let bialikVideoNode = videoNodeWith(resourceName: "Tsina_05",ofType: "mp4")
    bialikVideoNode.size = CGSize(width: kDizengofVideoWidth,height: kDizengofVideoHeight)
    bialikVideoNode.anchorPoint = CGPoint(x: 0.5,y: 0.0)

    // Make the video background transparent using an SKEffectNode,since chroma-key doesn't work on video
    let effectNode = SKEffectNode()
    effectNode.addChild(bialikVideoNode)
    effectNode.filter = colorCubeFilterForChromakey(hueAngle: 120)

    return effectNode
}

这是根据需要的结果:

相关文章

UITabBarController 是 iOS 中用于管理和显示选项卡界面的一...
UITableView的重用机制避免了频繁创建和销毁单元格的开销,使...
Objective-C中,类的实例变量(instance variables)和属性(...
从内存管理的角度来看,block可以作为方法的传入参数是因为b...
WKWebView 是 iOS 开发中用于显示网页内容的组件,它是在 iO...
OC中常用的多线程编程技术: 1. NSThread NSThread是Objecti...