【iOS ARKit】人形提取

 

     为解决人形分离和深度估计问题,ARKit 新增加了 Segmentation Buffer(人体分隔缓冲区)和Estimated Depth Data Buffer(深度估计缓冲区)两个缓冲区。人体分隔缓冲区作用类似于图形渲染管线中的 Stencil Buffer(模板缓冲区),用于区分人形区域与背景区域,它是一个像素级的缓冲区,用于精确地描述人形区域。

    人体分隔缓冲区用于标识人形区域,所以可以使用非常简单的结构,如使用1标识该像素是人形区域,而用。标识该像素为背景区。人体分隔缓冲区每帧都更新,所以可以动态地追踪摄像头采集的人形变化。

     既然人体分隔缓冲区标识了人形区域,我们也就可以利用该缓冲区提取出场景中的人形以便后续应用,如将人形图像通过网络传输到其他AR设备中,实现类似虚拟会议的效果;或者将人形图像放入虚拟世界中,营造更绚酷的体验;或者对提取的人形图像进行模糊和打马赛克等处理,实现以往只能使用绿幕才能实现的实时人形捕捉效果。

     为简单起见,本节我们直接获取人体分隔缓冲区数据并将其保存为图像,关键代码如代码如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//
//  HumanExtraction.swift
//  ARKitDeamo
//
//  Created by zhaoquan du on 2024/2/4.
//
 
import SwiftUI
import ARKit
import RealityKit
import Combine
import VideoToolbox
import AVFoundation
 
struct HumanExtraction: View {
     
    var viewModel = HumanExtractionViewModel()
     
    var arView: ARView {
        let arView = ARView(frame: .zero)
         
        return arView
    }
     
    var body: some View {
        HumanExtractionContainer(viewModel: viewModel)
            .overlay(
            VStack{
                Spacer()
                Button(action:{viewModel.catchHuman()}) {
                    Text("截取人形")
                        .frame(width:120,height:40)
                        .font(.body)
                        .foregroundColor(.black)
                        .background(Color.white)
                        .opacity(0.6)
                }
                .offset(y:-30)
                .padding(.bottom, 30)
            }
    )
        .edgesIgnoringSafeArea(.all)
    }
}
 
struct HumanExtractionContainer : UIViewRepresentable{
    
    var viewModel: HumanExtractionViewModel
     
     
    func makeUIView(context: Context) -> some ARView {
        let arView = ARView(frame: .zero)
         
       
         
        return arView
    }
     
    func updateUIView(_ uiView: UIViewType, context: Context) {
        guard ARWorldTrackingConfiguration.supportsFrameSemantics(.personSegmentation) else {
            return
        }
         
        let config = ARWorldTrackingConfiguration()
        config.frameSemantics = .personSegmentation
        uiView.session.delegate = viewModel
        uiView.session.run(config)
    }
     
     
     
}
 
class HumanExtractionViewModel: NSObject,ARSessionDelegate {
    var arFrame: ARFrame? = nil
    func session(_ session: ARSession, didUpdate frame: ARFrame) {
        arFrame = frame
    }
    func catchHuman(){
        if let segmentationBuffer = arFrame?.segmentationBuffer {
             
            if let uiImage = UIImage(pixelBuffer: segmentationBuffer)?.rotate(radians: .pi / 2) {
                UIImageWriteToSavedPhotosAlbum(uiImage, self, #selector(imageSaveHandler(image:didFinishSavingWithError:contextInfo:)), nil)
            }
        }
    }
    @objc func imageSaveHandler(image:UIImage,didFinishSavingWithError error:NSError?,contextInfo:AnyObject) {
        if error != nil {
            print("保存图片出错")
        } else {
            print("保存图片成功")
        }
    }
     
}
 
 
 
extension UIImage {
    public convenience init?(pixelBuffer:CVPixelBuffer) {
        var cgimage: CGImage?
         
        VTCreateCGImageFromCVPixelBuffer(pixelBuffer, options: nil, imageOut: &cgimage)
         
        if let cgimage = cgimage{
             
            self.init(cgImage: cgimage)
             
        }else{
            return nil
        }
    }
     
    func rotate(radians: CGFloat) -> UIImage {
        let rotatedSize = CGRect(origin: .zero, size: size).applying(CGAffineTransform(rotationAngle: CGFloat(radians))).integral.size
        UIGraphicsBeginImageContext(rotatedSize)
        if let context = UIGraphicsGetCurrentContext() {
            let origin = CGPoint(x: rotatedSize.width / 2.0, y: rotatedSize.height / 2.0)
            context.translateBy(x: origin.x, y: origin.y)
            context.rotate(by: radians)
             
            draw(in: CGRect(x: -origin.y, y: -origin.x, width: size.width, height: size.height))
             
            let rotateImage = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
             
            return rotateImage ?? self
             
        }
         
        return self
    }
}

     在代码 中,人体分隔缓冲区数据每帧都会更新,所以我们需要从 ARFrame 中实时获取值,然后将缓冲区中的数据转换成图像,由于缓冲区中的数据是直接对应硬件摄像头采集的图像数据,为与屏幕显示保持一致,需要对图像进行90°旋转,保存的图像如下右图所示。

     进行人形提取时,只是提取屏幕空间中的人形图像,无须使用深度信息,因此无须使用personSegmentation WithDepth 语义,只使用 personSegmentation 语义有助于提高应用性能。

     具体代码地址:https://github.com/duzhaoquan/ARkitDemo.git

posted @   不停奔跑的蜗牛  阅读(59)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示