visionOS图像识别项目:能否为检测到的图像添加边框?
为visionOS检测图像添加边框与可点击交互网格方案
当然可以实现这个需求,我们可以通过ImageAnchor提供的图像尺寸和空间姿态信息,生成匹配的边框实体,同时替换原来的球体为覆盖整个图像的可交互平面。以下是具体实现步骤:
1. 获取检测图像的空间参数
ImageAnchor的referenceImage.physicalSize包含了图像的物理尺寸,结合originFromAnchorTransform矩阵可以确定图像在空间中的位置和旋转姿态,这是生成边框和交互区域的核心依据。
2. 生成图像边框实体
用四个长方体拼接成图像的边框,确保边框刚好贴合检测到的图像:
private func createImageBorder(for anchor: ImageAnchor) -> ModelEntity { let imageSize = anchor.referenceImage.physicalSize let borderEntity = ModelEntity() // 边框厚度和偏移量,可根据需求调整 let borderThickness: Float = 0.01 let borderOffset: Float = 0.005 // 顶部边框 let topBorder = ModelEntity( mesh: .generateBox( width: imageSize.width + borderThickness * 2, height: borderThickness, depth: borderThickness ), materials: [UnlitMaterial(color: .systemBlue)] ) topBorder.transform.translation = SIMD3(0, (imageSize.height / 2) + borderOffset, 0) borderEntity.addChild(topBorder) // 底部边框 let bottomBorder = ModelEntity( mesh: .generateBox( width: imageSize.width + borderThickness * 2, height: borderThickness, depth: borderThickness ), materials: [UnlitMaterial(color: .systemBlue)] ) bottomBorder.transform.translation = SIMD3(0, -(imageSize.height / 2) - borderOffset, 0) borderEntity.addChild(bottomBorder) // 左侧边框 let leftBorder = ModelEntity( mesh: .generateBox( width: borderThickness, height: imageSize.height, depth: borderThickness ), materials: [UnlitMaterial(color: .systemBlue)] ) leftBorder.transform.translation = SIMD3(-(imageSize.width / 2) - borderOffset, 0, 0) borderEntity.addChild(leftBorder) // 右侧边框 let rightBorder = ModelEntity( mesh: .generateBox( width: borderThickness, height: imageSize.height, depth: borderThickness ), materials: [UnlitMaterial(color: .systemBlue)] ) rightBorder.transform.translation = SIMD3((imageSize.width / 2) + borderOffset, 0, 0) borderEntity.addChild(rightBorder) return borderEntity }
3. 创建覆盖图像的可交互平面
替换原来的球体,生成一个与检测图像尺寸完全匹配的平面作为点击交互载体:
private func createInteractivePlane(for anchor: ImageAnchor) -> ModelEntity { let imageSize = anchor.referenceImage.physicalSize // 生成匹配图像尺寸的平面 let planeMesh = MeshResource.generatePlane(width: imageSize.width, height: imageSize.height) // 半透明材质,既不遮挡原图像,又能看到交互区域 let planeMaterial = UnlitMaterial(color: UIColor.systemBlue.withAlphaComponent(0.2)) let interactiveEntity = ModelEntity(mesh: planeMesh, materials: [planeMaterial]) // 添加输入目标组件,允许接收交互事件 interactiveEntity.components.set(InputTargetComponent()) // 设置碰撞形状,确保点击区域完全覆盖图像 interactiveEntity.collision = CollisionComponent( shapes: [.generatePlane(width: imageSize.width, height: imageSize.height)] ) // 绑定点击事件(实体绑定比全局手势更精准) interactiveEntity.components.set(InputComponent()) interactiveEntity.sendEvent = { [weak self] event in guard case .tap = event.inputType else { return } self?.handleImageTap(anchor.referenceImage.name) } return interactiveEntity } private func handleImageTap(_ imageID: String) { // 这里添加你的交互反馈逻辑,比如高亮边框、弹出操作菜单等 print("Clicked image: \(imageID)") }
4. 更新原updateImage方法
修改你原来的updateImage函数,移除球体,添加边框和交互平面:
private func updateImage(_ anchor: ImageAnchor) { // 移除已存在的实体 if let existingEntity = entityMap[anchor.id] { existingEntity.removeFromParent() } let imageID = anchor.referenceImage.name detectedImageID = imageID print("Recognized image with ID: \(imageID)") // 创建父实体,统一管理边框和交互平面 let imageTrackingEntity = ModelEntity() // 添加交互平面 let interactivePlane = createInteractivePlane(for: anchor) imageTrackingEntity.addChild(interactivePlane) // 添加图像边框 let imageBorder = createImageBorder(for: anchor) imageTrackingEntity.addChild(imageBorder) // 同步图像锚点的空间姿态 if anchor.isTracked { imageTrackingEntity.transform = Transform(matrix: anchor.originFromAnchorTransform) imageTrackingEntity.isEnabled = true } // 存入实体映射并添加到场景 entityMap[anchor.id] = imageTrackingEntity contentEntity.addChild(imageTrackingEntity) // 保留你的动画逻辑,可改为给交互平面加动画 animateMesh(interactivePlane) }
关键注意点
- 确保你的参考图像在Assets中正确设置了Physical Size,否则生成的边框和交互区域会与实际图像不匹配。
- 交互事件用
sendEvent绑定到实体,比全局手势更精准,能确保只有点击到图像区域才触发反馈。 - 如果需要优化性能,可以复用Mesh和Material资源,避免每次检测都创建新的资源实例。
内容的提问来源于stack exchange,提问作者kartbouni
相关产品推荐
相关产品推荐

