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

如何让VNCoreMLRequest在横屏全屏范围内检测物体?

解决BreakfastFinder横屏模式下Vision检测区域不全的问题

问题根源

横屏时检测区域仅为居中竖屏比例区域,核心原因是AVCaptureSession的视频预设、视频方向与Vision的图像裁剪逻辑不匹配:

  • 固定使用.hd1920x1080(16:9横屏分辨率),但设置videoOrientation为.landscapeRight后,视频帧原始尺寸比例和屏幕横屏显示的映射关系出现偏差;
  • VNImageCropAndScaleOption.scaleFit会将图像缩放到模型输入尺寸时保留原始比例,导致横屏时左右超出模型比例的区域被裁剪,仅保留中间竖屏比例区域;
  • 锁定竖屏后旋转设备,系统强制让捕获帧适配了横屏显示比例,因此检测能覆盖全屏。

解决方案

1. 动态适配AVCaptureSession的预设与视频方向

根据屏幕方向切换对应的视频预设,确保捕获帧比例与当前屏幕方向一致,避免后续Vision处理时的不必要裁剪:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    
    // 先停止会话再修改配置
    session.stopRunning()
    
    // 更新视频方向
    let deviceOrientation = UIDevice.current.orientation
    if let connection = previewLayer.connection, connection.isVideoOrientationSupported {
        connection.videoOrientation = deviceOrientation.videoOrientation
    }
    
    // 根据屏幕宽高比选择对应预设
    let screenAspectRatio = size.width / size.height
    if screenAspectRatio > 1 { // 横屏
        session.sessionPreset = .hd1920x1080 // 16:9比例匹配横屏
    } else { // 竖屏
        session.sessionPreset = .hd1080x1920 // 9:16比例匹配竖屏
    }
    
    // 重启会话
    session.startRunning()
}

2. 修正Vision的裁剪缩放选项并匹配ROI

将imageCropAndScaleOption改为VNImageCropAndScaleOption.scaleFill,同时保持regionOfInterest为(x:0,y:0,width:1,height:1):

func setupVision() {
    guard let model = try? VNCoreMLModel(for: BreakfastFinder().model) else {
        fatalError("无法加载CoreML模型")
    }
    let request = VNCoreMLRequest(model: model) { [weak self] request, error in
        self?.processResults(for: request, error: error)
    }
    // 使用scaleFill替代scaleFit,避免裁剪区域
    request.imageCropAndScaleOption = .scaleFill
    request.regionOfInterest = CGRect(x: 0, y: 0, width: 1, height: 1)
    self.requests = [request]
}
  • scaleFill会将图像拉伸/压缩至模型输入尺寸,不会裁剪画面,且在捕获帧比例与模型输入比例匹配时,性能损耗极低(和scaleFit接近)。

3. 确保预览层始终适配屏幕尺寸

在视图布局更新时同步预览层的frame,避免预览显示与实际捕获帧的区域偏差:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    previewLayer.frame = view.bounds
}

关键说明

当捕获帧比例、屏幕显示比例、模型输入比例三者一致时,Vision的检测区域会完全匹配屏幕全屏,同时保持原有性能。锁定竖屏后旋转正常的本质就是系统帮你对齐了这三者的比例,上面的方案就是手动实现这个对齐逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 03:40:29