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

如何在ARKit中获取不含虚拟内容的原始摄像头输入截图?

获取ARSCNView的原始摄像头输入图像(不含虚拟对象)

嘿,这个需求我太熟了!之前做AR项目的时候也碰到过,用snapshot()确实会把渲染出来的虚拟对象一起拍进去,要只拿摄像头原生画面的话,其实可以直接从ARSession的帧数据入手,给你两个实用的方案:

方案一:通过ARSessionDelegate实时获取摄像头帧

ARSession会持续输出原始的摄像头捕获帧,我们可以通过代理方法拿到这些帧数据,再转换成和屏幕分辨率一致的UIImage:

  1. 首先给你的ARSCNView设置代理,并遵守ARSessionDelegate协议:
class YourViewController: UIViewController, ARSessionDelegate {
    @IBOutlet weak var arView: ARSCNView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        arView.session.delegate = self
    }
}
  1. 实现代理方法,获取摄像头帧并转换为UIImage:
func session(_ session: ARSession, didUpdateFrame frame: ARFrame) {
    // 获取原始摄像头捕获的CMSampleBuffer
    let imageBuffer = frame.capturedImage
    
    // 将CMSampleBuffer转换为UIImage
    guard let image = convertSampleBufferToUIImage(sampleBuffer: imageBuffer) else {
        return
    }
    
    // 调整图像尺寸与屏幕一致
    let screenSize = UIScreen.main.bounds.size
    let resizedImage = resizeImage(image: image, targetSize: screenSize)
    
    // 现在resizedImage就是你要的纯摄像头画面啦
    // 这里可以做你需要的操作,比如保存或者展示
}
  1. 补充两个关键的工具函数:
// CMSampleBuffer转UIImage,处理方向问题
private func convertSampleBufferToUIImage(sampleBuffer: CMSampleBuffer) -> UIImage? {
    guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return nil }
    
    CVPixelBufferLockBaseAddress(imageBuffer, .readOnly)
    defer { CVPixelBufferUnlockBaseAddress(imageBuffer, .readOnly) }
    
    let baseAddress = CVPixelBufferGetBaseAddress(imageBuffer)
    let width = CVPixelBufferGetWidth(imageBuffer)
    let height = CVPixelBufferGetHeight(imageBuffer)
    let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer)
    
    let colorSpace = CGColorSpaceCreateDeviceRGB()
    let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.noneSkipFirst.rawValue | CGBitmapInfo.byteOrder32Little.rawValue)
    
    guard let context = CGContext(data: baseAddress, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo.rawValue) else { return nil }
    
    guard let cgImage = context.makeImage() else { return nil }
    
    // 修正图像方向,匹配屏幕
    let image = UIImage(cgImage: cgImage, scale: 1.0, orientation: .right)
    return image
}

// 调整图像尺寸到目标大小
private func resizeImage(image: UIImage, targetSize: CGSize) -> UIImage {
    let size = image.size
    
    let widthRatio  = targetSize.width  / size.width
    let heightRatio = targetSize.height / size.height
    
    // 取最小比例保证图像完整,或者按需调整
    let scaleRatio = min(widthRatio, heightRatio)
    let newSize = CGSize(width: size.width * scaleRatio, height: size.height * scaleRatio)
    
    UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
    image.draw(in: CGRect(origin: .zero, size: newSize))
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    
    return newImage ?? image
}

方案二:直接从当前ARFrame获取(按需获取,不用实时监听)

如果不需要实时获取,只是在某个特定时机(比如用户点击按钮)获取的话,可以直接从arView.session.currentFrame拿数据:

@IBAction func captureCameraImage(_ sender: UIButton) {
    guard let frame = arView.session.currentFrame else {
        print("当前没有可用的AR帧")
        return
    }
    
    guard let image = convertSampleBufferToUIImage(sampleBuffer: frame.capturedImage) else {
        return
    }
    
    let screenSize = UIScreen.main.bounds.size
    let finalImage = resizeImage(image: image, targetSize: screenSize)
    
    // 处理finalImage
}

注意事项

  • 摄像头的原始分辨率通常比屏幕高,所以转换后一定要做尺寸调整,保证和屏幕显示一致
  • 图像方向的修正很重要,不然拿到的画面可能是旋转的,上面的代码里已经处理了.right方向,你可以根据自己的设备和需求调整
  • 如果需要更高性能的处理,可以考虑用Metal直接操作帧数据,但上面的方法已经足够应对大多数场景啦

内容的提问来源于stack exchange,提问作者jfriesenhahn

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 08:34:25