如何利用ARKit获取捕获图像中物体的物理尺寸(宽、高)?
解决ARKit中真实物体物理高度计算问题
你的思路方向是对的,但现有代码存在几个关键问题:直接用屏幕边缘的CGPoint并不对应物体的上下边缘,且仅依赖featurePoint的hitTest稳定性差(特征点可能不在物体表面)。下面提供几个可行的优化方案:
方案1:手动标记物体上下边缘(最直接可靠)
让用户通过触摸操作标记物体的顶部和底部,获取对应屏幕坐标后再做3D位置检测:
// 存储用户标记的点 private var markedPoints: [CGPoint] = [] // 触摸事件处理 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touch = touches.first, markedPoints.count < 2 else { markedPoints.removeAll() return } let touchPoint = touch.location(in: self) markedPoints.append(touchPoint) if markedPoints.count == 2 { calculateHeightFromMarkedPoints() } } private func calculateHeightFromMarkedPoints() { guard let topPos = getPosition(point: markedPoints[0]), let bottomPos = getPosition(point: markedPoints[1]) else { print("无法获取3D位置") return } let distanceM = topPos.distance(to: bottomPos) let distanceMm = round(distanceM * 1000) print("物体高度:\(distanceMm)mm") markedPoints.removeAll() } // 替换hitTest为更可靠的raycast(ARKit 3.0+) private func getPosition(point: CGPoint) -> SCNVector3? { guard let session = self.session else { return nil } let raycastQuery = session.raycastQuery(from: point, allowing: .estimatedPlane, alignment: .any) guard let result = session.raycast(raycastQuery).first else { // 如果平面检测失败, fallback到featurePoint let hitTestResults = self.hitTest(point, types: .featurePoint) return hitTestResults.first.map { SCNVector3.positionFrom(matrix: $0.worldTransform) } } return SCNVector3.positionFrom(matrix: result.worldTransform) } // 保留你的扩展方法 extension SCNVector3 { func distance(to destination: SCNVector3) -> CGFloat { let dx = destination.x - x let dy = destination.y - y let dz = destination.z - z return CGFloat(sqrt(dx*dx + dy*dy + dz*dz)) } static func positionFrom(matrix: matrix_float4x4) -> SCNVector3 { let column = matrix.columns.3 return SCNVector3(column.x, column.y, column.z) } }
方案2:利用相机内参+已知距离计算(无需标记点)
如果你已经知道相机到物体的距离,可以通过相似三角形原理计算,无需依赖hitTest:
func calculateHeightFromCameraDistance(objectPixelHeight: CGFloat, cameraDistanceM: CGFloat) -> CGFloat { guard let camera = session.currentFrame?.camera else { return 0 } let intrinsics = camera.intrinsics // 获取相机焦距(单位:像素) let focalLengthY = CGFloat(intrinsics[1][1]) // 真实高度 = (像素高度 × 距离) / 焦距 let realHeightM = (objectPixelHeight * cameraDistanceM) / focalLengthY return realHeightM * 1000 // 转换为毫米 } // 使用示例:假设你通过物体检测得到了它在图像中的像素高度 // let objectPixelHeight = 200 // 比如物体占据200像素高度 // let heightMm = calculateHeightFromCameraDistance(objectPixelHeight: objectPixelHeight, cameraDistanceM: 1.5)
注意:需要先通过物体检测(比如Core ML、Vision框架)获取物体在图像中的像素高度objectPixelHeight
方案3:结合平面检测优化hitTest
如果物体放置在平面上(比如桌面),可以先检测平面,再将底部点约束到平面上,顶部点用hitTest物体表面:
private func calculateHeightWithPlane() { // 假设已经检测到平面,获取平面锚点 guard let planeAnchor = session.currentFrame?.anchors.first(where: { $0 is ARPlaneAnchor }) as? ARPlaneAnchor else { print("未检测到平面") return } // 物体底部在平面上的屏幕点(需要手动标记或检测) let bottomScreenPoint = CGPoint(x: self.bounds.midX, y: self.bounds.maxY - 100) // 顶部点 let topScreenPoint = CGPoint(x: self.bounds.midX, y: self.bounds.minY + 100) // 底部3D位置:投影到平面上 let bottomPos = SCNVector3(planeAnchor.center.x, planeAnchor.center.y, planeAnchor.center.z) // 顶部3D位置:hitTest获取 guard let topPos = getPosition(point: topScreenPoint) else { return } let heightMm = round(topPos.distance(to: bottomPos) * 1000) print("物体高度:\(heightMm)mm") }
内容的提问来源于stack exchange,提问作者Volodymyr Buha
相关产品推荐
相关产品推荐

