iOS自定义YOLOv8模型仅横屏可检测,竖屏失效问题求助
解决iOS竖屏模式下YOLOv8 Core ML模型检测失效问题
问题根源
核心矛盾是设备方向、相机捕获图像方向、模型训练时的图像方向三者不匹配:一方面传递给Core ML模型的图像不符合训练预期,导致检测失效;另一方面UI图层的固定旋转逻辑仅适配横屏,引发检测框、标签显示错位。
分步解决方案
1. 修正EXIF方向映射(核心:让模型拿到正确方向的图像)
假设你的YOLOv8模型是基于竖屏图像训练的,替换exifOrientationFromDeviceOrientation方法,确保传递给Vision框架的图像方向与训练时一致:
public func exifOrientationFromDeviceOrientation() -> CGImagePropertyOrientation { let orientation = UIDevice.current.orientation switch orientation { case .portrait: return .right // 相机捕获的横屏图像转90度顺时针,匹配竖屏训练样本 case .portraitUpsideDown: return .left // 转270度顺时针 case .landscapeLeft: return .upMirrored // 横屏Home键右侧,对应训练时的横屏镜像图像 case .landscapeRight: return .down // 横屏Home键左侧,对应训练时的横屏图像 case .faceUp, .faceDown, .unknown: // 设备平放时 fallback 到竖屏方向 return .right } }
若模型是基于横屏图像训练的,需反向调整映射关系。
2. 动态调整检测图层几何变换(解决UI显示错位)
原方法固定旋转90度仅适配横屏,改为根据设备方向动态计算旋转角度:
func updateLayerGeometry() { let bounds = rootLayer.bounds var scale: CGFloat var rotationAngle: CGFloat = 0.0 // 根据设备方向确定旋转角度 let orientation = UIDevice.current.orientation switch orientation { case .portrait, .portraitUpsideDown: rotationAngle = .pi / 2.0 case .landscapeLeft, .landscapeRight: rotationAngle = 0.0 default: rotationAngle = .pi / 2.0 } // 计算等比例缩放系数 let xScale: CGFloat = bounds.size.width / bufferSize.height let yScale: CGFloat = bounds.size.height / bufferSize.width scale = fmax(xScale, yScale) if scale.isInfinite { scale = 1.0 } CATransaction.begin() CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions) // 组合旋转、缩放、镜像变换 var transform = CGAffineTransform(rotationAngle: rotationAngle) transform = transform.scaledBy(x: scale, y: -scale) detectionOverlay.setAffineTransform(transform) detectionOverlay.position = CGPoint(x: bounds.midX, y: bounds.midY) CATransaction.commit() }
3. 修正文本图层旋转(解决标签垂直显示问题)
根据设备方向动态调整文本图层的尺寸与旋转:
func createTextSubLayerInBounds(_ bounds: CGRect, identifier: String, confidence: VNConfidence) -> CATextLayer { let textLayer = CATextLayer() textLayer.name = "Object Label" let formattedString = NSMutableAttributedString(string: String(format: "\(identifier)\nConfidence: %.2f", confidence)) let largeFont = UIFont(name: "Helvetica", size: 10.0)! formattedString.addAttributes([NSAttributedString.Key.font: largeFont], range: NSRange(location: 0, length: identifier.count)) textLayer.string = formattedString // 根据设备方向适配文本图层 let orientation = UIDevice.current.orientation if orientation.isPortrait { textLayer.bounds = CGRect(x: 0, y: 0, width: bounds.size.height - 10, height: bounds.size.width - 10) textLayer.setAffineTransform(CGAffineTransform(rotationAngle: .pi / 2.0).scaledBy(x: 1.0, y: -1.0)) } else { textLayer.bounds = CGRect(x: 0, y: 0, width: bounds.size.width - 10, height: bounds.size.height - 10) textLayer.setAffineTransform(CGAffineTransform.identity) } textLayer.position = CGPoint(x: bounds.midX, y: bounds.midY) textLayer.shadowOpacity = 0.7 textLayer.shadowOffset = CGSize(width: 2, height: 2) textLayer.foregroundColor = CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [0.0, 0.0, 0.0, 1.0]) textLayer.contentsScale = UIScreen.main.scale return textLayer }
4. 监听设备方向变化,实时更新图层
在viewDidLoad中添加方向监听,确保旋转时图层同步更新:
override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(deviceOrientationChanged), name: UIDevice.orientationDidChangeNotification, object: nil) UIDevice.current.beginGeneratingDeviceOrientationNotifications() } @objc func deviceOrientationChanged() { updateLayerGeometry() } deinit { NotificationCenter.default.removeObserver(self) UIDevice.current.endGeneratingDeviceOrientationNotifications() }
额外验证步骤
- 确认YOLOv8训练时的图像方向,若为横屏需反向调整EXIF映射与图层逻辑
- 打印
exifOrientation值,验证不同设备方向对应正确的CGImagePropertyOrientation - 检查
bufferSize是否与相机预览分辨率一致
内容的提问来源于stack exchange,提问作者sks
相关产品推荐
相关产品推荐

