You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 22:32:10