如何在ARKit中获取不含虚拟内容的原始摄像头输入截图?
获取ARSCNView的原始摄像头输入图像(不含虚拟对象)
嘿,这个需求我太熟了!之前做AR项目的时候也碰到过,用snapshot()确实会把渲染出来的虚拟对象一起拍进去,要只拿摄像头原生画面的话,其实可以直接从ARSession的帧数据入手,给你两个实用的方案:
方案一:通过ARSessionDelegate实时获取摄像头帧
ARSession会持续输出原始的摄像头捕获帧,我们可以通过代理方法拿到这些帧数据,再转换成和屏幕分辨率一致的UIImage:
- 首先给你的ARSCNView设置代理,并遵守
ARSessionDelegate协议:
class YourViewController: UIViewController, ARSessionDelegate { @IBOutlet weak var arView: ARSCNView! override func viewDidLoad() { super.viewDidLoad() arView.session.delegate = self } }
- 实现代理方法,获取摄像头帧并转换为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就是你要的纯摄像头画面啦 // 这里可以做你需要的操作,比如保存或者展示 }
- 补充两个关键的工具函数:
// 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
相关产品推荐
相关产品推荐

