【iOS ARKit】人形提取

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

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

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

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

//
//  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 语义有助于提高应用性能。

具体代码地址:GitHub - duzhaoquan/ARkitDemo

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/377360.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

洛谷C++简单题小练习day9—[AHOI2017]寻找探监点

day9--[AHOI2017]寻找探监点--2.7 习题概述 题目描述 一个nn 的网格图(标号由 1,1 开始)上有 m 个探测器,每个探测器有个探测半径 r ,问这 nn 个点中有多少个点能被探测到。 输入格式 第一行 3 个整数 n,m,r。 接下来 m 行&…

国内首个openEuler师训营圆满结营! 麒麟信安助力培养国产操作系统高质量师资人才

2024年1月22日,全国首个openEuler师训营圆满结营!旨在深化产教融合,加速开源教育走进高校,提高师资队伍openEuler专业能力及实践教学水平。 本次师训营由长沙市大数据产业链、长沙市新一代自主安全计算系统产业链指导&#xff0c…

【Docker】Docker Image(镜像)

文章目录 一、Docker镜像是什么?二、镜像生活案例三、为什么需要镜像四、镜像命令详解docker rmidocker savedocker loaddocker historydocker image prune 五、镜像操作案例六、镜像综合实战实战一、离线迁移镜像实战二、镜像存储的压缩与共享 一、Docker镜像是什么…

顺序表、链表相关OJ题(2)

创作不易,友友们给个三连吧!! 一、旋转数组(力扣) 经典算法OJ题:旋转数组 思路1:每次挪动1位,右旋k次 时间复杂度:o(N^2) 右旋最好情况:k是n的倍数…

naiveui 上传图片遇到的坑 Upload

我在开发图片上传功能, 需要手动触发上传 但是我调用它内部自定义submit方法, 结果接口一直在报错400 我反反复复的测试了好就, 确定了就是我前端的问题,因为之前一直在做后端的错误排查, 以为是编译问题(因为之前也出现过这个问题) 好 , 我把其中一个参数类型改为String类型, …

c++设计模式之装饰器模式

作用 为现有类增加功能 案例说明 class Car { public:virtual void show()0; };class Bmw:public Car { public:void show(){cout<<"宝马汽车>>"<<endl;} };class Audi:public Car { public:void show(){cout<<"奥迪汽车>>&q…

Java玩转《啊哈算法》解密回文之栈

菩萨清凉月&#xff0c;常游毕竟空&#xff0c;众生心垢净&#xff0c;菩提影现中。 这目录 这开头这代码地址栈案例代码优化建议类似扩展 这开头 各位女士们&#xff0c;先生们好&#xff01;本人最近在看《啊哈算法》&#xff0c;这本书写的确实还可以&#xff0c;很有趣味性…

代码随想录算法训练营第28天 | 93.复原IP地址 ,78.子集 ,90.子集II

回溯章节理论基础&#xff1a; https://programmercarl.com/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html 93.复原IP地址 题目链接&#xff1a;https://leetcode.cn/problems/restore-ip-addresses/ 思路&#xff1a; 这是切割问题&am…

2024年2月8日 十二生肖 今日运势

小运播报&#xff1a;2024年2月8日&#xff0c;星期四&#xff0c;农历腊月廿九 &#xff08;甲辰年丙寅月壬寅日&#xff09;&#xff0c;法定工作日。 红榜生肖&#xff1a;马、猪、狗 需要注意&#xff1a;龙、蛇、猴 喜神方位&#xff1a;正南方 财神方位&#xff1a;正…

基于swing和cf的推荐相似度SQL实现

对于cf和swing算法的介绍可以参考ItemCF的演进&#xff1a;狭义 VS 广义 基于cf的推荐相似度 这里介绍这样一个场景&#xff0c;我们有了大量的电商购买数据&#xff0c;希望通过cf算法计算不同的类目之间的相似度&#xff0c;以方便对用户购买进行兴趣探索。 使用SQL实现需要…

10.0 Zookeeper 权限控制 ACL

zookeeper 的 ACL&#xff08;Access Control List&#xff0c;访问控制表&#xff09;权限在生产环境是特别重要的&#xff0c;所以本章节特别介绍一下。 ACL 权限可以针对节点设置相关读写等权限&#xff0c;保障数据安全性。 permissions 可以指定不同的权限范围及角色。 …

【STL】:priority_queue介绍和模拟实现

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下有关priority_queue的使用&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从入门到精通…

C# ONNX使用入门教程

背景 有新入坑的老哥不太了解C# onnx 运行的机理&#xff0c;我这边详细介绍一下&#xff0c;之前直接放官方的样例有点草率了。 准备[python环境] 1、要使用onnx&#xff0c;首先我们就自己生成一个onnx文件&#xff0c;请大家准备一下以下需要的[python]环境 python 版本…

探索设计模式的魅力:揭秘享元模式-轻松实现资源高效利用的秘密武器

设计模式专栏&#xff1a;http://t.csdnimg.cn/U54zu 目录 引言&#xff1a; 一、简介 二、实现资源的极致利用 公共自行车与享元模式的智慧共享 HOW 三、案例探讨 3.1 场景 3.2 不用模式实现&#xff1a;一坨坨代码实现 3.3 痛点 3.4 解决方案分析 注意 四、深入享…

Qt多线程与SocketTCP的简单实现

1.相关说明 多线程实现Qt的socket编程实现客户端发送文件&#xff0c;服务端接收文件&#xff0c;并且在客户端设置了心跳&#xff0c;用于监控服务端是否存活。因为Qt中socket套接字发送数据&#xff0c;会先把数据发送至缓冲区中&#xff0c;在发送数据过程中&#xff0c;soc…

寒假学习第24天---PythonPoc基础编写(二)

提示&#xff1a;所分享内容仅用于每一个爱好者之间的技术讨论及教育目的&#xff0c;所有渗透及工具的使用都需获取授权&#xff0c;禁止用于违法途径&#xff0c;否则需自行承担&#xff0c;本作者不承担相应的后果。 文章目录 前言一、 目标二、过程思路实践开始 总结完整代…

Java基于微信小程序的驾校报名小程序,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

腾讯云游戏服务器购买入口,详细配置精准报价

2024年更新腾讯云游戏联机服务器配置价格表&#xff0c;可用于搭建幻兽帕鲁、雾锁王国等游戏服务器&#xff0c;游戏服务器配置可选4核16G12M、8核32G22M、4核32G10M、16核64G35M、4核16G14M等配置&#xff0c;可以选择轻量应用服务器和云服务器CVM内存型MA3或标准型SA2实例&am…

创建网站的具体步骤是什么?

创建网站的具体步骤是什么 一.领取一个免费域名和SSL证书&#xff0c;和CDN 1.打开网站链接&#xff1a;https://www.rainyun.com/z22_ 2.在网站主页上&#xff0c;您会看到一个"登陆/注册"的选项。 3.点击"登陆/注册"&#xff0c;然后选择"微信登…

假期刷题打卡--Day26

1、MT1212乘法表 请编写一个简单程序&#xff0c;输出九九乘法表。输入n&#xff0c;就输出乘法表到n的地方。 格式 输入格式&#xff1a; 输入整型 输出格式&#xff1a; 输出整型。形式如&#xff1a;1*11 样例 1 输入&#xff1a; 5输出&#xff1a; 1*11 2*12 …