VNBarcodeObservation的结果中observation.boundingBox 是什么类型?
大家好,我的开源项目PakePlus可以将网页/Vue/React项目打包为桌面/手机应用并且小于5M只需几分钟,官网地址:pakeplus.com
![]()
observation.boundingBox 的类型是 CGRect。
CGRect 结构
CGRect 是 Core Graphics 框架中的结构体,表示一个矩形区域:
public struct CGRect {
public var origin: CGPoint
public var size: CGSize
}
在 Vision 框架中的特性
在 Vision 框架中,boundingBox 使用归一化坐标系统:
let barcodeRequest = VNDetectBarcodesRequest { request, error in
guard let results = request.results as? [VNBarcodeObservation] else { return }
for observation in results {
let boundingBox: CGRect = observation.boundingBox
print("boundingBox: \(boundingBox)")
// 访问具体属性
print("原点: \(boundingBox.origin)") // CGPoint
print("尺寸: \(boundingBox.size)") // CGSize
print("x: \(boundingBox.origin.x)") // CGFloat
print("y: \(boundingBox.origin.y)") // CGFloat
print("宽度: \(boundingBox.size.width)") // CGFloat
print("高度: \(boundingBox.size.height)") // CGFloat
// 其他便捷属性
print("最小X: \(boundingBox.minX)")
print("最小Y: \(boundingBox.minY)")
print("最大X: \(boundingBox.maxX)")
print("最大Y: \(boundingBox.maxY)")
print("中心X: \(boundingBox.midX)")
print("中心Y: \(boundingBox.midY)")
}
}
归一化坐标系统
Vision 框架的 boundingBox 使用归一化坐标:
-
坐标范围: (0.0, 0.0) 到 (1.0, 1.0)
-
原点: 左下角 (与 UIKit 的左上角不同!)
-
x: 从左到右 (0.0 = 左边缘, 1.0 = 右边缘)
-
y: 从下到上 (0.0 = 下边缘, 1.0 = 上边缘)
// 示例 boundingBox 值
let exampleBox = CGRect(x: 0.2, y: 0.3, width: 0.4, height: 0.3)
// 表示:
// - 从图像左边 20% 的位置开始
// - 从图像底部 30% 的位置开始
// - 宽度为图像的 40%
// - 高度为图像的 30%
坐标转换
由于归一化坐标和 UIKit 坐标系统不同,需要进行转换:
1. 转换为图像像素坐标
func convertToImageCoordinates(boundingBox: CGRect, imageSize: CGSize) -> CGRect {
// Vision → 图像坐标 (左下角原点)
let imageRect = VNImageRectForNormalizedRect(
boundingBox,
Int(imageSize.width),
Int(imageSize.height)
)
return imageRect
}
2. 转换为 UIKit 视图坐标
func convertToViewCoordinates(boundingBox: CGRect, viewSize: CGSize) -> CGRect {
// Vision → UIKit 坐标 (左上角原点)
let viewRect = CGRect(
x: boundingBox.origin.x * viewSize.width,
y: (1 - boundingBox.origin.y - boundingBox.size.height) * viewSize.height,
width: boundingBox.size.width * viewSize.width,
height: boundingBox.size.height * viewSize.height
)
return viewRect
}
完整的使用示例
import Vision
import UIKit
class BarcodeDetector {
func processBarcodeObservation(_ observation: VNBarcodeObservation,
imageSize: CGSize,
targetView: UIView) {
let boundingBox: CGRect = observation.boundingBox
// 1. 打印原始 boundingBox
print("原始 boundingBox: \(boundingBox)")
// 2. 转换为图像坐标
let imageRect = VNImageRectForNormalizedRect(
boundingBox,
Int(imageSize.width),
Int(imageSize.height)
)
print("图像坐标: \(imageRect)")
// 3. 转换为视图坐标 (用于在屏幕上绘制)
let viewRect = convertToViewRect(boundingBox: boundingBox,
viewSize: targetView.bounds.size)
print("视图坐标: \(viewRect)")
// 4. 在界面上绘制边界框
drawBoundingBox(on: targetView, rect: viewRect)
}
private func convertToViewRect(boundingBox: CGRect, viewSize: CGSize) -> CGRect {
return CGRect(
x: boundingBox.origin.x * viewSize.width,
y: (1 - boundingBox.origin.y - boundingBox.size.height) * viewSize.height,
width: boundingBox.size.width * viewSize.width,
height: boundingBox.size.height * viewSize.height
)
}
private func drawBoundingBox(on view: UIView, rect: CGRect) {
// 移除之前的边界框
view.layer.sublayers?.removeAll(where: { $0.name == "boundingBox" })
// 创建新的边界框图层
let boxLayer = CAShapeLayer()
boxLayer.name = "boundingBox"
boxLayer.frame = rect
boxLayer.borderColor = UIColor.green.cgColor
boxLayer.borderWidth = 2.0
boxLayer.backgroundColor = UIColor.clear.cgColor
view.layer.addSublayer(boxLayer)
}
}
重要注意事项
-
坐标系统差异: Vision 使用左下角原点,UIKit 使用左上角原点
-
归一化范围: 坐标值在 0.0-1.0 范围内
-
空矩形检查: 检查 boundingBox 是否有效
-
边界处理: 确保转换后的坐标在有效范围内
// 检查 boundingBox 是否有效
if boundingBox.isNull || boundingBox.isInfinite {
print("无效的 boundingBox")
return
}
// 检查是否在有效范围内
if boundingBox.minX < 0 || boundingBox.maxX > 1 ||
boundingBox.minY < 0 || boundingBox.maxY > 1 {
print("boundingBox 超出有效范围")
}
总结:observation.boundingBox 是 CGRect 类型,使用归一化坐标系统表示检测对象在图像中的位置和大小,需要进行适当的坐标转换才能在 UIKit 界面中使用。