IOS ARKit进行图像识别

先讲一下基础控涧,资源的话可以留言,抽空我把它传到GitHub上,这里没写收积分,竟然充值才能下载,我下载也要充值,牛!
ARSCNView 可以理解画布或者场景
1 配置 ARWorldTrackingConfiguration AR追踪识别相关
ARFaceTrackingConfiguration 面部识别
2 加载资源 图片资源就不多讲,手机拍照去掉底图就可以了,3d模型需要带linda的ipad pro或者iphone pro构建
一共两种一种是识别图片,一种是识别3d模型,感觉图片识别效果较好,但是麻烦点是获取现实世界空间坐标,而且对运动物体还是不是很友好,所以后来准备看一下CoreML相关


 let configuartion = ARWorldTrackingConfiguration()
var images :[UIImage] = []
        for i in 1...19{
            let image = UIImage(named: "AR\(i)")
            images.append(image!)
        }
        //加载图片资源,通过图片名
        configuartion.detectionImages = loadedImagesFromDirectoryContents(images)
        configuartion.maximumNumberOfTrackedImages = 1

//加载3d资源,3d资源文件如下图所示
if let referenceObj = ARReferenceObject.referenceObjects(inGroupNamed: "AR Resource Group", bundle: nil){
            print("")
            configuartion.detectionObjects = referenceObj
        }
        
          configuartion.isAutoFocusEnabled = true // 确保自动对焦开启
//       
        configuartion.planeDetection = .horizontal
        configuartion.isLightEstimationEnabled = true
         sceneView.session.run(configuartion, options: [.resetTracking,.removeExistingAnchors])
        
        //遵守的代理协议ARSessionDelegate 会话协议
         sceneView.session.delegate = self
         //ARSCNViewDelegate scnview场景协议
        sceneView.delegate = self
       
        //显示调试参数
        sceneView.debugOptions = [SCNDebugOptions.showFeaturePoints]



//通过图片名加载图片
 func loadedImagesFromDirectoryContents(_ images: [UIImage]) -> Set<ARReferenceImage>{

        var index = 0
        var customReferenceSet = Set<ARReferenceImage>()

        images.forEach { (downloadedImage) in

            //1. Convert The UIImage To A CGImage
            guard let cgImage = downloadedImage.cgImage else { return }

            //2. Get The Width Of The Image
            let imageWidth = CGFloat(cgImage.width)

            //3. Create A Custom AR Reference Image With A Unique Name
            let customARReferenceImage = ARReferenceImage(cgImage, orientation: CGImagePropertyOrientation.up, physicalWidth: imageWidth)
            customARReferenceImage.name = "MyCustomARImage\(index)"

            //4. Insert The Reference Image Into Our Set
            customReferenceSet.insert(customARReferenceImage)

            print("ARReference Image == \(customARReferenceImage)")

            index += 1


        }
        //5. Return The Set
        return customReferenceSet

    }

3d资源文件,文件格式是.arobject,该文件是通过带Linda的iPad pro设备扫描生成的,详情参考代码demo
请添加图片描述

ARSessionDelegate 协议

/*
     会话失败
     */
    func session(_ session: ARSession, didFailWithError error: any Error) {
        print("didFailWithError")
    }
    
    /*
     相机更改了追踪模式
     */
    func session(_ session: ARSession, cameraDidChangeTrackingState camera: ARCamera) {
        
        let state = camera.trackingState
        
        print("cameraDidChangeTrackingState")
    }
    /*
        当会话中断时调用此方法。
       会话将被中断,不再能跟踪什么时候
       它无法接收所需的传感器数据。 当视频捕获中断时,
       例如当应用程序被发送到后台或当有的时候
       多个前台应用程序(请参阅AVCaptureSessionInterruptReason)。
       在中断结束之前,不会传送额外的帧更新。
     */
    func sessionWasInterrupted(_ session: ARSession) {
        print("sessionWasInterrupted")
    }
    
    /*
        当会话中断结束时调用。
       会话将从最后一次已知的状态继续运行一次
       中断已经结束。 如果设备移动,锚点将不对齐。
       为避免这种情况,一些应用程序可能想要重置跟踪(请参阅ARSessionRunOptions)。
    
     */
    func sessionInterruptionEnded(_ session: ARSession) {
        print("sessionInterruptionEnded")
    }
    
    /*
     当会话输出新的音频采样缓冲区时
     */
    func session(_ session: ARSession, didOutputAudioSampleBuffer audioSampleBuffer: CMSampleBuffer) {
        print("didOutputAudioSampleBuffer")
    }
    
    func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {
        //更新锚点位置,存入的锚点信息,也可以是图片锚点
        for anchor in anchors {
                guard let objectAnchor = anchor as? ARObjectAnchor else { continue }

                // 查找对应的框节点
                if let frameNode = sceneView.scene.rootNode.childNode(withName: "frame", recursively: true) {
//                    frameNode.position = SCNVector3(
//                        objectAnchor.transform.columns.3.x,
//                        objectAnchor.transform.columns.3.y,
//                        objectAnchor.transform.columns.3.z
//                    )
                    frameNode.simdTransform = anchor.transform
                }
            }
        
//        guard let trackedAnchor = trackedAnchor else{return}
//        
//        for anchor in anchors{
//            if anchor.identifier == trackedAnchor.identifier{
//                let newTransform = anchor.transform
//                let position = SCNVector3(newTransform.columns.3.x,
//                                          newTransform.columns.3.y,
//                                          newTransform.columns.3.z)
//                
//                DispatchQueue.main.async {
//                    self.trackNode?.position = position
//                }
//            }
//        }
//        print("didupdate session")
//        //跟踪目标物体
//        for anchor in anchors {
//              if let objectAnchor = anchor as? ARObjectAnchor {
//                  print("Object moved to: \(objectAnchor.transform)")
//                  
//                  // 更新虚拟对象的位置
//                  if let node = sceneView.node(for: objectAnchor) {
//                      let transform = objectAnchor.transform
//                      node.simdTransform = transform
//                  }
//              }
//          }
    }
    
    func session(_ session: ARSession, didUpdate frame: ARFrame) {
       
        DispatchQueue.global().async {
            self.handleFrame(session: session, frame: frame)
        }
    }

ARSCNViewDelegate代理

 /*
     实现这个为给定的锚点提供一个自定义节点。
      @discussion 此节点将自动添加到场景图。如果未实现此方法,将自动创建一个节点。如果返回nil,锚点将被忽略。
      @param renderer渲染场景的渲染器。
      @param anchor添加的锚点。
      @return将映射到锚点或nil的节点
     */
//    func renderer(_ renderer: any SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
//        return nil
//    }
    /*
     当新节点已映射到给定的锚点时调用。
       @param renderer 渲染场景的渲染器。
       @param node 映射到锚点的节点。
       @param anchor 添加的锚点。
     */
    func renderer(_ renderer: any SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
//        print("添加新的节点锚点数据到场景中")

  if let objectAnchor = anchor as? ARObjectAnchor {
              print("Detected object: \(objectAnchor.referenceObject.name ?? "Unknown")")
           
            // 创建一个框架来圈住物体
                    let frameNode = createFrame(for: objectAnchor.referenceObject)
                    frameNode.position = SCNVector3(
                        objectAnchor.transform.columns.3.x,
                        objectAnchor.transform.columns.3.y,
                        objectAnchor.transform.columns.3.z
                    )
                    sceneView.scene.rootNode.addChildNode(frameNode)
          }
        
      
        
//        if let imageAnchor = anchor as? ARImageAnchor{
//            let image = imageAnchor.referenceImage
//            
//            if let imagename = image.name, imagename.hasPrefix("MyCustomARImage"){
//                // 创建一个虚拟对象(如盒子)来标记目标位置
                let box = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0)
                let material = SCNMaterial()
                material.diffuse.contents = UIColor.red
                box.materials = [material]

                let boxNode = SCNNode(geometry: box)
                boxNode.position = SCNVector3(0, 0, 0)
                node.addChildNode(boxNode)
//                DispatchQueue.main.async {
//                    self.showToast()
//                }
//            }
//        }
}


 /*
     当节点将使用给定锚点的数据进行更新时调用。
      @param renderer 渲染场景的渲染器。
      @param node 将被更新的节点。
      @param anchor 即将更新的锚点。
     */
    
    func renderer(_ renderer: any SCNSceneRenderer, willUpdate node: SCNNode, for anchor: ARAnchor) {
//        print("即将更新新节点锚点数据")
    }
    /*
     当节点将使用给定锚点的数据进行更新时调用。
      @param renderer 渲染场景的渲染器。
      @param node 将被更新的节点。
      @param anchor 已更新的锚点。
     */
    func renderer(_ renderer: any SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
//        print("已经更新节点锚点数据")

}


    /*
     当映射节点已从给定锚点的场景图中删除时调用。
       @param renderer渲染场景的渲染器。
       @param node已删除的节点。
       @param anchor已删除的锚点。
     */
    func renderer(_ renderer: any SCNSceneRenderer, didRemove node: SCNNode, for anchor: ARAnchor) {
        print("删除节点")
    }

以上是整个代码流程
图片识别部分如下

 private func handleFrame(session: ARSession,frame: ARFrame){
        let i = frame.timestamp - (self.currentFrmae?.timestamp ?? 0)
//        print("time i\(i)")
        if i < 1{
            return
        }
        self.currentFrmae = frame
        guard let frameImg = getCurrentFrameImage(from: session) else{return}
        var isMatched:Bool = false
        for img in self.images{
//            if compareImages(image1: frameImg, image2: img) == 1{
//                isMatched = true
//                break
//            }
            let im1 = frameImg
            let im2 = img
            matchImages(image1: frameImg, image2: img) { [weak self]isok in
                if isok{
                    DispatchQueue.main.async {
                        self?.showToast()
                    }
                }
            }
        }
        
        if isMatched{
            DispatchQueue.main.async {
                
                self.showToast()
            }
        }
    }
    
   
//获取当前会话帧数据
    func getCurrentFrameImage(from session: ARSession) -> UIImage? {
        guard let currentFrame = session.currentFrame else {
            print("当前没有 ARFrame")
            return nil
        }
        let pixelBuffer = currentFrame.capturedImage
        let ciImage = CIImage(cvPixelBuffer: pixelBuffer)
        let context = CIContext()
        guard let cgImage = context.createCGImage(ciImage, from: ciImage.extent) else {
            return nil
        }
        return UIImage(cgImage: cgImage)
    }


//对比图片2
    func matchImages(image1: UIImage, image2: UIImage, completion: @escaping (Bool) -> Void) {
        guard let cgImage1 = image1.cgImage, let cgImage2 = image2.cgImage else {
            completion(false)
            return
        }
        //使用自定义检测任务
        let request1 = VNGenerateImageFeaturePrintRequest()
        let request2 = VNGenerateImageFeaturePrintRequest()
        
        let request3 = VNDetectRectanglesRequest()
            request3.minimumConfidence = 0.8
            request3.minimumAspectRatio = 0.1
            request3.maximumAspectRatio = 1.0
            request3.quadratureTolerance = 10
        
        let handler1 = VNImageRequestHandler(cgImage: cgImage1, options: [:])
        let handler2 = VNImageRequestHandler(cgImage: cgImage2, options: [:])
        let handler3 = VNImageRequestHandler(cgImage: cgImage1, options: [:])

        do {
            try handler1.perform([request1])
            try handler2.perform([request2])
            try handler3.perform([request3])

            guard let featurePrint1 = request1.results?.first as? VNFeaturePrintObservation,
                  let featurePrint2 = request2.results?.first as? VNFeaturePrintObservation else {
                completion(false)
                return
            }
            
            if let observations = request3.results as? [VNRectangleObservation], !observations.isEmpty{
                // 假设目标区域是第一个匹配的矩形
                let boundingBox = observations.first?.boundingBox
                print("boundRext: \(boundingBox)")
            }

            var distance: Float = 0
            try featurePrint1.computeDistance(&distance, to: featurePrint2)
            print("匹配值 \(distance)")
            if distance < 0.8{ //距离0.8m
                print("图片相似")
                
            }
            completion(distance < 0.8) // 设定相似度阈值
        } catch {
            print("匹配失败:\(error)")
            completion(false)
        }
    }

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

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

相关文章

golang debug调试

1. 本地调试 1&#xff1a;Add Configurations 添加配置文件&#xff08;Run kind &#xff1a;Directory&#xff09; 2&#xff1a;进入run运行窗口 3&#xff1a;debug断点调试模式 1. Resume Program (继续运行) 图标: ▶️ 或 ► 快捷键: F9&#xff08;Windows/Linux&a…

联想YOGA Pro 14s至尊版电脑找不到独立显卡(N卡)问题,也无法安装驱动的问题

问题描述 电脑是联想YOGA Pro 14s至尊版&#xff0c;电脑上装的独立显卡是4060&#xff0c;一直是能够使用独立显卡的。然而有两次突然就找不到显卡了&#xff0c;NVIDIA CONTROL PANEL也消失了&#xff0c;而且也无法安装驱动。具体表现如下&#xff1a; 无法连接外接显示器…

Python酷库之旅-第三方库Pandas(250)

目录 一、用法精讲 1181、pandas.tseries.offsets.BusinessMonthEnd.is_on_offset方法 1181-1、语法 1181-2、参数 1181-3、功能 1181-4、返回值 1181-5、说明 1181-6、用法 1181-6-1、数据准备 1181-6-2、代码示例 1181-6-3、结果输出 1182、pandas.tseries.offse…

【机器学习】机器学习的基本分类-监督学习-逻辑回归-Sigmoid 函数

Sigmoid 函数是一种常用的激活函数&#xff0c;尤其在神经网络和逻辑回归中扮演重要角色。它将输入的实数映射到区间 (0, 1)&#xff0c;形状类似于字母 "S"。 1. 定义与公式 Sigmoid 函数的公式为&#xff1a; 特点 输出范围&#xff1a;(0, 1)&#xff0c;适合用…

点云处理中obb算法原理和法向量求解方法

主要数学原理PCA PCA&#xff08;Principal Component Analysis&#xff0c;主成分分析&#xff09;是数据分析中的一种重要技术&#xff0c;通过它可以将高维数据投影到低维空间&#xff0c;找到数据的主要结构。在点云分析中&#xff0c;PCA 可以帮助我们提取点云数据中的主…

四:工具、环境准备-compute node

一&#xff1a;工具、环境准备-controller node 二&#xff1a;OpenStack环境准备-controller node 三&#xff1a;安装服务-controller node 四&#xff1a;工具、环境准备-compute node 五&#xff1a;OpenStack环境准备-compute node 六&#xff1a;安装服务-compute node 七…

力扣1382:将二叉搜索树便平衡

给你一棵二叉搜索树&#xff0c;请你返回一棵 平衡后 的二叉搜索树&#xff0c;新生成的树应该与原来的树有着相同的节点值。如果有多种构造方法&#xff0c;请你返回任意一种。 如果一棵二叉搜索树中&#xff0c;每个节点的两棵子树高度差不超过 1 &#xff0c;我们就称这棵二…

NGO-CNN-BiGRU-Attention北方苍鹰算法优化卷积双向门控循环单元时间序列预测,含优化前后对比

NGO-CNN-BiGRU-Attention北方苍鹰算法优化卷积双向门控循环单元时间序列预测&#xff0c;含优化前后对比 目录 NGO-CNN-BiGRU-Attention北方苍鹰算法优化卷积双向门控循环单元时间序列预测&#xff0c;含优化前后对比预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介…

微信小程序Webview与H5通信

背景 近期有个微信小程序需要用到web-view嵌套H5的场景&#xff0c;该应用场景需要小程序中频繁传递数据到H5进行渲染&#xff0c;且需要保证页面不刷新。 由于微信小程序与H5之间的通信限制比较大&#xff0c;显然无法满足于我的业务场景 探索 由于微信小程序与webview的环境是…

18. C++STL 4(vector的使用, 空间增长, 迭代器失效详解)

⭐本篇重点&#xff1a;vector容器的使用详解 ⭐本篇代码&#xff1a;c学习/08.vector_test 橘子真甜/c-learning-of-yzc - 码云 - 开源中国 (gitee.com) 目录 一. vector的介绍 二. vector的使用 2.1 vector的定义 * 2.2 vector的迭代器和遍历 a operator[]访问 b vect…

NAT拓展

NAT ALG&#xff08;NAT应用级网&#xff09; 为某些应用层协议&#xff0c;因为其报文内容可能携带IP相关信息&#xff0c;而普通NAT转化无法将这些IP转化&#xff0c;从而导致协议无法正常运行 例如FTP&#xff0c;DHCP&#xff0c;RSTP&#xff0c;ICMP&#xff0c;IPSEC…

私有库gitea安装

一 gitea是什么 Gitea是一款自助Git服务&#xff0c;简单来说&#xff0c;就是可以一个私有的github。 搭建很容易。 Gitea依赖于Git。 类似Gitea的还有GitHub、Gitee、GitLab等。 以下是安装步骤。 二 安装sqilite 参考&#xff1a; 在windows上安装sqlite 三 安装git…

从零开始理解JVM:对象的生命周期之对象销毁(垃圾回收)

一、JVM参数 在学垃圾回收器之前&#xff0c;我们先要知道&#xff0c;jvm参数是怎么回事。因为配置各种回收器&#xff0c;必须对应各种参数设置。 标准参数&#xff08;-&#xff09; 所有的JVM实现都必须实现这些参数的功能&#xff0c;而且向后兼容 -help-version 非标准参…

C# 数据类型详解:掌握数据类型及操作为高效编码奠定基础

本文将带你深入了解C#中各种数据类型的特点、用途和最佳实践&#xff0c;让你不仅能熟练运用基本类型&#xff0c;还能掌握如何在实际项目中做出最合适的选择。 目录 C#基本语法 C#数据类型 C#类型转换 C#变量常量 C#基本语法 在学习C#之前我们要先知道C#的基础构建是由哪些…

后端-mybatis的多对多

首先准备两张表学生表和课程表&#xff0c;一个学生可以选多个课程&#xff0c;一门课程也可以被多个学生选择。 再建一个学生表和课程表的中间表&#xff0c;包含学生id和课程id。 我们拿查询所有学生 和他们所选的课程为例&#xff0c;写多对多&#xff08;其实就是一对多&a…

05《存储器层次结构与接口》计算机组成与体系结构 系列课

目录 存储器层次结构概述 层次结构的定义 存储器的排名 存储器接口 处理器与存储器的速度匹配 存储器接口的定义 存储器访问命中率 两种接口 第1种方式&#xff1a;并行 命中率的计算 存储器访问时间 第2种方式&#xff1a;逐级 结语 大家好&#xff0c;欢迎回来。…

软件测试——性能测试工具JMeter

1.JMeter介绍 Apache JMeter是一款纯java编写负载功能测试和性能测试开源工具软件。JMeter小巧轻便且免费&#xff0c;逐渐成为了主流的性能测试工具&#xff0c;是每个测试人员都必须要掌握的工具之一。 环境要求&#xff1a; ​ 需要Java8或者更高的版本。 1.1 JMeter的下…

ARP表、MAC表、路由表的区别和各自作用

文章目录 ARP表、MAC表、路由表的区别和各自作用同一网络内:ARP表request - 请求reply - 响应 MAC地址在同一网络内,交换机如何工作? 不同网络路由表不同网络通信流程PC1到路由器路由器到PC2 简短总结 ARP表、MAC表、路由表的区别和各自作用 同一网络内: ARP作用: 让发送方知…

【Azure Cache for Redis】Redis的导出页面无法配置Storage SAS时通过az cli来完成

问题描述 在Azure Redis的导出页面&#xff0c;突然不能配置Storage Account的SAS作为授权方式。 image.png 那么是否可以通过AZ CLI或者是Powershell来实现SAS的配置呢&#xff1f; 问题解答 可以的。使用 az redis export 可以实现 az redis export --container --prefix[--a…

【AI系统】昇腾 AI 架构介绍

昇腾 AI 架构介绍 昇腾计算的基础软硬件是产业的核⼼&#xff0c;也是 AI 计算能⼒的来源。华为&#xff0c;作为昇腾计算产业⽣态的⼀员&#xff0c;是基础软硬件系统的核⼼贡献者。昇腾计算软硬件包括硬件系统、基础软件和应⽤使能等。 而本书介绍的 AI 系统整体架构&#…