VNBarcodeObservation的结果中observation.boundingBox 是什么类型?

大家好,我的开源项目PakePlus可以将网页/Vue/React项目打包为桌面/手机应用并且小于5M只需几分钟,官网地址:pakeplus.com

observation.boundingBox 的类型是 CGRect

CGRect 结构

CGRect 是 Core Graphics 框架中的结构体,表示一个矩形区域:

swift 复制代码
public struct CGRect {
    public var origin: CGPoint
    public var size: CGSize
}

在 Vision 框架中的特性

在 Vision 框架中,boundingBox 使用归一化坐标系统

Swift 复制代码
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 = 上边缘)

Swift 复制代码
// 示例 boundingBox 值
let exampleBox = CGRect(x: 0.2, y: 0.3, width: 0.4, height: 0.3)
// 表示:
// - 从图像左边 20% 的位置开始
// - 从图像底部 30% 的位置开始  
// - 宽度为图像的 40%
// - 高度为图像的 30%

坐标转换

由于归一化坐标和 UIKit 坐标系统不同,需要进行转换:

1. 转换为图像像素坐标

Swift 复制代码
func convertToImageCoordinates(boundingBox: CGRect, imageSize: CGSize) -> CGRect {
    // Vision → 图像坐标 (左下角原点)
    let imageRect = VNImageRectForNormalizedRect(
        boundingBox, 
        Int(imageSize.width), 
        Int(imageSize.height)
    )
    return imageRect
}

2. 转换为 UIKit 视图坐标

Swift 复制代码
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
}

完整的使用示例

Swift 复制代码
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)
    }
}

重要注意事项

  1. 坐标系统差异: Vision 使用左下角原点,UIKit 使用左上角原点

  2. 归一化范围: 坐标值在 0.0-1.0 范围内

  3. 空矩形检查: 检查 boundingBox 是否有效

  4. 边界处理: 确保转换后的坐标在有效范围内

Swift 复制代码
// 检查 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.boundingBoxCGRect 类型,使用归一化坐标系统表示检测对象在图像中的位置和大小,需要进行适当的坐标转换才能在 UIKit 界面中使用。
大家好,我是1024小神,技术群 / 私活群 / 股票群 或 交朋友 都可以私信我。 如果你觉得本文有用,一键三连 (点赞、评论、关注),就是对我最大的支持~

相关推荐
weixin_427771619 小时前
cursor 智能commit
前端
努力的小陈^O^9 小时前
问题:Spring循环依赖问题排查与解决
java·开发语言·前端
徐_三岁9 小时前
127.0.0.1 和 localhost 有什么区别?
前端
沛沛老爹10 小时前
Web开发者转型AI:Agent Skills版本控制与管理实战——从Git到AI技能仓库
java·前端·人工智能·git·架构·rag
yyt36304584110 小时前
TypeScript { [key: string]: unknown } 索引签名写法和 Record 替代
前端·javascript·vue.js·typescript·ecmascript·es6
揽昕10 小时前
判断对象是否含有某个属性
开发语言·前端·javascript
前端小趴菜0511 小时前
TypeScript
前端·typescript
getapi12 小时前
在宝塔面板中部署 Vue 项目打包后的 dist 文件作为前端
前端·javascript·vue.js
59678515412 小时前
css浮动
前端·css·html
我想发发发12 小时前
已经安装了ROS环境却还是报错`ModuleNotFoundError: No module named ‘rclpy‘`
前端·人工智能·chrome·机器人