>source

저는 ARKit에서 비디오를 크로마 키하려고 하고 있으며 @Felix가 여기에서 한 것과 매우 유사합니다.

하지만, 동영상이 표시되어야 하는 경우(이 경우 AR 참조 이미지가 감지되면)[SceneKit] 오류: 픽셀 버퍼(CVPixelBufferRef)를 가져올 수 없습니다.오류가 발생하고 비디오가 더 이상 재생되지 않습니다. 구현하기 전에 재생되었습니다.chromaKeyMaterial. AR 참조 이미지가 감지된 직후부터 시작하는 내 code는 다음과 같습니다.

DispatchQueue.main.async {
let filePath= Bundle.main.path(forResource: "wigz", ofType: "mp4")
let videoURL= NSURL(fileURLWithPath: filePath!)
let player= AVPlayer(url: videoURL as URL)
let spriteKitScene= SKScene(size: CGSize(width: 640, height: 480))
let videoSpriteKitNode= SKVideoNode(avPlayer: player)
let videoNode= SCNNode()
videoNode.geometry= SCNPlane(width: imageAnchor.referenceImage.physicalSize.width,
              height: imageAnchor.referenceImage.physicalSize.height)
videoNode.eulerAngles= SCNVector3(-Float.pi/2, 0, 0)
//Use spritekit with videonode inside
spriteKitScene.scaleMode= .aspectFit
videoSpriteKitNode.position= CGPoint(x: spriteKitScene.size.width /2,
                      y: spriteKitScene.size.height /2)
videoSpriteKitNode.size= spriteKitScene.size
videoSpriteKitNode.yScale= -1.0
videoSpriteKitNode.play()
//Loop video
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: player.currentItem, queue: .main) { _ in
    player.seek(to: kCMTimeZero)
    player.play()
}
spriteKitScene.addChild(videoSpriteKitNode)
videoNode.geometry?.firstMaterial?.diffuse.contents= spriteKitScene
videoNode.geometry?.firstMaterial?.isDoubleSided= true
let chromaKeyMaterial= ChromaKeyMaterial()
chromaKeyMaterial.diffuse.contents= player
videoNode.geometry!.materials= [chromaKeyMaterial]
node.addChildNode(videoNode)
self.imageDetectView.scene.rootNode.addChildNode(node)
}

ChromaKeyMaterial.swift 파일에서 다음 행을 다음과 같이 변경했습니다.

float maskY= 0.0 * c_colorToReplace.r + 1.0 * c_colorToReplace.g + 0.0 * c_colorToReplace.b;
float maskCr= 0.7132 * (c_colorToReplace.r -maskY);
float maskCb= 0.5647 * (c_colorToReplace.b -maskY);
float Y= 0.0 * textureColor.r + 1.0 * textureColor.g + 0.0 * textureColor.b;
float Cr= 0.7132 * (textureColor.r -Y);
float Cb= 0.5647 * (textureColor.b -Y);

순수한 녹색을 크로마 키아웃하기 위한 노력이지만 이것이 올바른 접근 방식인지 확신할 수 없습니다.

도움을 주시면 감사하겠습니다!

SKScene 및 SKVideoNode를 사용하는 SceneKit의 최신 버전에서는 필요하지 않습니다. AVPlayer를 SCNMaterialProperty 인스턴스의 내용으로 직접 설정할 수 있습니다.

mnuages2021-11-25 02:14:32

@mnuages ​​흥미롭습니다! 확인하겠습니다! 감사 해요.

nerk2021-11-25 02:14:32

@mnuages ​​와우. 훨씬 간단하고 더 잘 실행되는 것 같지만 내 기억이 실패하거나 위약이 될 수 있습니다. 팁 고마워!

nerk2021-11-25 02:14:32
  • 답변 # 1

    RealityKit 2 사용 -iOS14

    RealityKit을 사용하면 크로마 셰이더를 만들기 위해 금속을 사용해야 한다고 생각합니다.
    나는 아직 메탈에 대해 잘 몰라서 어떻게 만드는지 말할 수 없지만 RealityKit을 사용하여 AR에서 크로마 키 비디오를 재생하는 다른 방법을 찾았습니다.

    iOS14부터는 비디오 자료를 텍스처로 사용할 수 있습니다.ModelEntity.

    크로마의 경우 몇 가지 추가 단계가 필요합니다.

    • 먼저 동영상 자산을 변환하고 크로마 키를 제거해야 합니다.
    • 그런 다음 플레이어에서 이 자산을 로드하고 modelEntity(iOS 14)의 새로운 videomaterial 속성을 사용합니다.

    우리는 Yu Ao의 이 놀라운 패키지를 가져오기 시작했습니다.
    https://github.com/MetalPetal/MetalPetal/issues/289

    패키지를 가져오는 것을 잊지 마십시오.MetalPetal 가져오기

    이 code는 다음과 같습니다.

    
    //in the viewmodel you process the asset and create the player
    let context= try! MTIContext(device: MTLCreateSystemDefaultDevice()!)
    let chromaKeyBlendFilter= MTIChromaKeyBlendFilter()
    let color= MTIColor(red: 0.998, green: 0.0, blue: 0.996, alpha: 1)
    //let backgroundColor= MTLClearColor(red: 0, green: 0, blue: 0, alpha: 0)
    let backgroundColor= MTIColor(red: 0.0, green: 0.0, blue: 0, alpha: 0)
    chromaKeyBlendFilter.color= color
    chromaKeyBlendFilter.smoothing= 0.001
    chromaKeyBlendFilter.thresholdSensitivity= 0.4//0.475
    chromaKeyBlendFilter.inputBackgroundImage= MTIImage(color: backgroundColor, sRGB: false, size: videoSize)
    let composition= MTIVideoComposition(asset: asset, context: context, queue: DispatchQueue.main, filter: { request in
        guard let sourceImage= request.anySourceImage else {
            return MTIImage(color: backgroundColor, sRGB: false, size: videoSize)
        }
        return FilterGraph.makeImage(builder: { output in
            sourceImage=> chromaKeyBlendFilter.inputPorts.inputImage
            chromaKeyBlendFilter=> output
        })!
    })
    videoPlayerItem= AVPlayerItem(asset: asset)
    videoPlayerItem.videoComposition= composition.makeAVVideoComposition()
    let player= AVPlayer(playerItem: videoPlayerItem)
    player.volume= 0.5
    //player.play()
    

    RealityKit 2.0(Xcode 12 및 iOS 14)에서 비디오 텍스처를 사용할 수 있습니다. 설정 방법에 대한 Andy Jazz의 이 답변을 참조하세요.

  • 답변 # 2

    이것을 알아냈습니다. 색상을 잘못 키아웃하도록 설정했는데(그리고 잘못된 위치 facepalm에서도) 약간 지연시키지 않으면 비디오가 재생되지 않는 버그가 있는 것 같습니다. 해당 버그는 수정된 것으로 추정되지만 그렇지 않은 것 같습니다.

    여기 누군가가 관심이 있는 경우 수정하고 정리한 code입니다(@mnuages의 팁을 포함하도록 편집됨).

    //Get Video URL and create AV Player
    let filePath= Bundle.main.path(forResource: "VIDEO_FILE_NAME", ofType: "VIDEO_FILE_EXTENSION")
    let videoURL= NSURL(fileURLWithPath: filePath!)
    let player= AVPlayer(url: videoURL as URL)
    //Create SceneKit videoNode to hold the spritekit scene.
    let videoNode= SCNNode()
    //Set geometry of the SceneKit node to be a plane, and rotate it to be flat with the image
    videoNode.geometry= SCNPlane(width: imageAnchor.referenceImage.physicalSize.width,
                  height: imageAnchor.referenceImage.physicalSize.height)
    videoNode.eulerAngles= SCNVector3(-Float.pi/2, 0, 0)
    //Set the video AVPlayer as the contents of the video node's material.
    videoNode.geometry?.firstMaterial?.diffuse.contents= player
    videoNode.geometry?.firstMaterial?.isDoubleSided= true
    //Alpha transparancy stuff
    let chromaKeyMaterial= ChromaKeyMaterial()
    chromaKeyMaterial.diffuse.contents= player
    videoNode.geometry!.materials= [chromaKeyMaterial]
    //video does not start without delaying the player
    //playing the video before just results in [SceneKit] Error: Cannot get pixel buffer (CVPixelBufferRef)
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.001) {
        player.seek(to:CMTimeMakeWithSeconds(1, 1000))
        player.play()
    }
    //Loop video
    NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: player.currentItem, queue: .main) { _ in
        player.seek(to: kCMTimeZero)
        player.play()
    }
    //Add videoNode to ARAnchor
    node.addChildNode(videoNode)
    //Add ARAnchor node to the root node of the scene
    self.imageDetectView.scene.rootNode.addChildNode(node)
    

    여기에 크롬 키 머티리얼이 있습니다.

    import SceneKit
    public class ChromaKeyMaterial: SCNMaterial {
    public var backgroundColor: UIColor {
        didSet { didSetBackgroundColor() }
    }
    public var thresholdSensitivity: Float {
        didSet { didSetThresholdSensitivity() }
    }
    public var smoothing: Float  {
        didSet { didSetSmoothing() }
    }
    public init(backgroundColor: UIColor= .green, thresholdSensitivity: Float= 0.50, smoothing: Float= 0.001) {
        self.backgroundColor= backgroundColor
        self.thresholdSensitivity= thresholdSensitivity
        self.smoothing= smoothing
        super.init()
        didSetBackgroundColor()
        didSetThresholdSensitivity()
        didSetSmoothing()
        //chroma key shader is based on GPUImage
        //https://github.com/BradLarson/GPUImage/blob/master/framework/Source/GPUImageChromaKeyFilter.m
        let surfaceShader=
        """
    uniform vec3 c_colorToReplace;
    uniform float c_thresholdSensitivity;
    uniform float c_smoothing;
    #pragma transparent
    #pragma body
    vec3 textureColor= _surface.diffuse.rgb;
    float maskY= 0.2989 * c_colorToReplace.r + 0.5866 * c_colorToReplace.g + 0.1145 * c_colorToReplace.b;
    float maskCr= 0.7132 * (c_colorToReplace.r -maskY);
    float maskCb= 0.5647 * (c_colorToReplace.b -maskY);
    float Y= 0.2989 * textureColor.r + 0.5866 * textureColor.g + 0.1145 * textureColor.b;
    float Cr= 0.7132 * (textureColor.r -Y);
    float Cb= 0.5647 * (textureColor.b -Y);
    float blendValue= smoothstep(c_thresholdSensitivity, c_thresholdSensitivity + c_smoothing, distance(vec2(Cr, Cb), vec2(maskCr, maskCb)));
    float a= blendValue;
    _surface.transparent.a= a;
    """
        //_surface.transparent.a= a;
        shaderModifiers= [
            .surface: surfaceShader,
        ]
    }
    required public init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    //setting background color to be keyed out
    private func didSetBackgroundColor() {
        //getting pixel from background color
        //let rgb= backgroundColor.cgColor.components!.map{Float($0)}
        //let vector= SCNVector3(x: rgb[0], y: rgb[1], z: rgb[2])
        let vector= SCNVector3(x: 0.0, y: 1.0, z: 0.0)
        setValue(vector, forKey: "c_colorToReplace")
    }
    private func didSetSmoothing() {
        setValue(smoothing, forKey: "c_smoothing")
    }
    private func didSetThresholdSensitivity() {
        setValue(thresholdSensitivity, forKey: "c_thresholdSensitivity")
    }
    }
    

    안녕하세요, "node"와 "imageAnchor"를 초기화하는 방법을 알려주시겠습니까?

    Tomas2021-11-25 09:26:32
  • 이전 printing : Gtk.Widget을 프린터로 인쇄하는 가장 좋은 방법은 무엇입니까?
  • 다음 cassandra : 카산드라와 G1 가비지 컬렉터는 세계 이벤트(STW)를 중지합니다.