简单了解下,ARKit是苹果的增强虚拟与现实的框架,是苹果在2017年WWDC推出的AR开发平台,而Vision框架是 Apple 在 WWDC 2017 推出的图像识别框架
我们下面就来做下物体识别的例子,首先从官网下载模型
然后目前我们要做的就是点击屏幕的中间部分需要有小球产生以及显示出识别的物体是什么的文字,效果如下所示,它貌似识别成了大炮..
既然我们需要去通过点击屏幕去显示出小球,那么我们肯定是需要为其去添加手势,这里需要注意的是如果我们创建的是ARKit的框架,那么在Main.Storyboard当中默认设置的就是ARSCNView,ARSCNView是3D的AR场景视图,用来显示现实还有虚拟物件的视图,如果我们想使⽤SceneKit 作为渲染引擎的话就必须去使用它
//点击屏幕需要手势
func regiterGestureRecognizers()
{
let tapGes = UITapGestureRecognizer(target: self, action: #selector(tapped))
self.sceneView.addGestureRecognizer(tapGes)
}
然后再来看下手势的处理方法
@objc func tapped(recognizer:UIGestureRecognizer){
//创建一个sceneView,当前画面的SceneView也就是截图,我手势点击到的图片
//ARSCNView是3D的AR场景视图
let sceneView = recognizer.view as! ARSCNView
//手指碰到手机,有用的范围就是中间部分,因为我们要识别的是屏幕中央的物件是什么
let touchLocation = self.sceneView.center;
/**
ARSession是管理设备相机和增强现实需要的运动处理相关的共享对象
一个ARSession对象可以协调完成ARkit的主要过程来帮助你完成一个增强现实的体验。这些过程包括了从运动分析硬件中读取数据、控制设备的相机、对相机捕获的图像进行分析。(会话)session对象会综合所有结果,在你的现实世界和创建的虚拟世界之间创建一个对应的关系。从而构建出一个AR世界。你可以理解为现实与虚拟共存的第三世界。
每一个AR体验都需要创建一个ARSession对象。你可以选择用SRSCNView或者ARSKView对象来构建AR视图显示部分,该视图对象包括一个ARSession实例。如果您为AR内容构建自己的渲染器,则你需要自己实例化和维护ARSession对象。
运行一个会话需要配置一个ARSessionConfiguration类或其子类类或其子类ARWorldTrackingSessionConfiguration的实例。这些类决定了ARKit如何追踪设备与现实世界之间的相对位置与运动数据,因此会影响你创建的AR体验的质量。
*/
//如果没有可用的用东西就直接返回,当前屏幕上没有可用用的帧数
guard let currentFrame = sceneView.session.currentFrame else
{
return
}
//识别物体的特征点
//在屏幕当中的某点去捕捉特征值
let hitTestResults = sceneView.hitTest(touchLocation, types: .featurePoint)
if hitTestResults.isEmpty{return}
//我们只需要对第一个点结果
guard let hitTestResult = hitTestResults.first else {return}
//这个结果我们需要全局使用
self.hitTestResult = hitTestResult;
//这个结果我们去拆成一个一个的像素
//当前帧画面所拿到的图片,需要把拿到的图片转换为像素
//模型是直接去拿缓冲区里面的东西做处理的
let pixelBuffer = currentFrame.capturedImage;
//点击了图片转换为像素之后就进入了下面的方法
performVisionRequest(pixelBuffer: pixelBuffer)
}
下面我们再来看下performVisionRequest方法,就是去创建图形分析请求,然后去检测
func performVisionRequest(pixelBuffer:CVPixelBuffer){
//下面就要用到模型了
let visionModel = try! VNCoreMLModel(for: self.resentModel.model)
//VNCoreMLModel 只是用于 Vision 请求的 Core ML 模型的容器。
//然后声明一个模型识别请求:
//VNCoreMLRequest 是一个图形分析请求,使用 Core ML 模型作为参数。它的完成回调中带一个 request 参数和一个错误对象。
//识别完成之后会调用后面的闭包
let request = VNCoreMLRequest(model: visionModel){request,error in
//如果有错误就什么都不干
if error != nil {return}
// //其返回的结果是一个VNClassificationObservation数组,每一个VNClassificationObservation都是一个识别的结果,把结果给拿出来,把结果给拿出来
guard let observations = request.results else{return}
//把第一个结果给拿出来,VNClassificationObservation这个为模型里面的黑盒子,用来处理我们的所有运算结果
/*
首先判断 request.results 是否是一个 VNClassificationObservation 对象数组,当 Core ML 模型是一个分类器,而不是预测器和图像处理器的时候会返回这种类型的数组
VNClassificationObservation 有两个属性: identifier — 一个字符串 — 和 confidence — 一个 0 和 1 之间的数字 — 表示分类正确性的概率。当使用对象检测模型时,你可能是通过大于某个可信度来识别对象的,比如 30%.
*/
let observation = observations.first as! VNClassificationObservation
print("Name \(observation.identifier) and confidence is\(observation.confidence)")
//展示预测的结果,因为CoreML本身是不知道是什么东西的,只能预测
DispatchQueue.main.async {
//调用displayPredictions函数渲染AR的效果
self.displayPredictions(text:observation.identifier)
}
}
//模型需要224*224,取出中间这一块来,进行喂食
//VNImageCropAndScaleOptionCenterCrop
//告诉视觉算法如何缩放输入图像的可选设置。
request.imageCropAndScaleOption = .centerCrop
//拿到结果
self.visionRequests = [request]
//选择镜像,因为摄像头采集进来的像素是左右相反的
//数据在里面是阵列的方式
let imageRequestHandler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, orientation: .upMirrored, options: [:])
//这个处理的过程是非常耗费时间的
DispatchQueue.global().async {
//把图像拿来进行运行
/*
创建一个物体检测请求 VNRequest
根据数据源(pixelBuffer 或者 UIImage)创建一个 VNImageRequestHandler
调用[VNImageRequestHandler performRequests] 执行检测
*/
try! imageRequestHandler.perform(self.visionRequests)
}
}
然后我们再去看下调用displayPredictions函数渲染AR的效果的函数
func displayPredictions(text:String){
//创建节点
let node = createText(text: text)
//其中将 ARHitTestResult 信息转换成三维坐标
//也就是将现实世界中的x、y、z轴换算到手机当中的x、y、z轴
//世界坐标矩阵
/**
SCNVector3Make将ARHitTestResult的worldTransform.columns[3].x,worldTransform.columns[3].y,worldTransform.columns[3].z做成SCNVector3,即可得到3维坐标
*/
node.position = SCNVector3(self.hitTestResult.worldTransform.columns.3.x,
self.hitTestResult.worldTransform.columns.3.y,
self.hitTestResult.worldTransform.columns.3.z)
//把AR结果给展示出来,scene是一个SCNScene对象代表三维场景及其内容。渲染用于显示的场景
self.sceneView.scene.rootNode.addChildNode(node)
}
然后我们再去看下创建节点的方法createText
func createText(text:String) -> SCNNode
{
//父节点,就和NSObject的概念是一样的
let parentNode = SCNNode()
//圆形的几何图形,在SceneKit的世界里面单位是以米为单位的,0.01代表的就是1公分的小球形状
let sphere = SCNSphere(radius: 0.01)
/*
每个SCNMaterial有8个视觉特性,分别对应在明暗过程中不同的贴图效果。每个视觉特性,都是SCNMaterialProperty的一个实例,可以提供一个颜色或纹理等2D内容。SCNMaterial的lightingModelName也会影响到渲染的最终效果
Material --> 材料
可以使用firstMaterial或者materials属性添加一个或多个贴图。多个几何图形可以用相同的贴图,这样修改贴图就可以同时改变所有使用它的几何
*/
let sphereMaterial = SCNMaterial();
sphereMaterial.diffuse.contents = UIColor.orange
sphere.firstMaterial = sphereMaterial
//创建一个球形的节点
let sphereNode = SCNNode(geometry: sphere)
//文字,参数1位文字,参数2为厚度
let textGeo = SCNText(string: text, extrusionDepth: 0)
//设置文字居中
textGeo.alignmentMode = kCAAlignmentCenter
//我们不一定需要去创建一个贴图去设置
textGeo.firstMaterial?.diffuse.contents = UIColor.orange
//specular指定了材质的镜面反射
textGeo.firstMaterial?.specular.contents = UIColor.white
//设置是否是正反两面都能看
textGeo.firstMaterial?.isDoubleSided = true
//设置字体的大小
textGeo.font = UIFont(name: "Futura", size: 0.15)
let textNode = SCNNode(geometry: textGeo)
//设置缩放
textNode.scale = SCNVector3Make(0.2, 0.2, 0.2)
parentNode.addChildNode(sphereNode)
parentNode.addChildNode(textNode)
return parentNode
}
下面是关于ViewController当中的一些成员变量
//分析的结果
var visionRequests = [VNRequest]()
//拿到模型
var resentModel = Resnet50();
//点击之后得到的结果就是截屏下来的那张图片
//根据2D坐标取得3D模型:ARHitTestResult
var hitTestResult:ARHitTestResult!;