写点什么

iOS MachineLearning 系列(6)—— 视频中的物体轨迹分析

作者:珲少
  • 2023-04-27
    上海
  • 本文字数:2618 字

    阅读完需:约 9 分钟

轨迹分析是比物体追踪更上层的一种应用。Vision 框架中提供了检测视频中多个物体的运动轨迹等能力,在健身,体育类应用中非常有用。


轨迹检测需要一系列的运动状态来分析,因此这类的请求是有状态的,有状态的请求可以被句柄多次调用,其会自动记录之前的状态,从而进行轨迹路径分析。需要注意,在进行轨迹检测时,要保证摄像机的相对静止,镜头的移动可能会影响检测的准确性。


在日常生活中,我们可以使用轨迹检测来进行投球的矫正,球类落点的推测等等。

1 - 解析视频中的物体飞行轨迹

轨迹检测需要保存状态,因此其传入的图像分析参数需要为包含 CMTime 信息的 CMSampleBuffer 数据。对于一个视频文件,我们首先要做的是将其中的图像帧解析出来,即获取到 CMSampleBuffer 数据。示例代码如下:


func detectTrajectories() {    // 视频资源url    let videoURL = URL(fileURLWithPath: Bundle.main.path(forResource: "video2", ofType: ".mov")!)    // 读取视频资源    let asset =  AVAsset(url: videoURL)    guard let videoTrack = asset.tracks(withMediaType: .video).first else { return }    // 获取帧率    let frameRate = videoTrack.nominalFrameRate    // 获取总时长    let frameDuration = CMTime(seconds: 1 / Double(frameRate), preferredTimescale: CMTimeScale(NSEC_PER_SEC))    // 解析参数    let assetReaderOutputSettings: [String: Any] = [        kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA    ]    // 解析输出类实例    let assetReaderOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: assetReaderOutputSettings)    // 创建视频reader实例    let assetReader = try! AVAssetReader(asset: asset)    // 添加输出对象    assetReader.add(assetReaderOutput)    // 开始解析    if assetReader.startReading() {        // 读取帧        while let sampleBuffer = assetReaderOutput.copyNextSampleBuffer() {            autoreleasepool {                if CMSampleBufferDataIsReady(sampleBuffer) {                    let timestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)                    // 进行轨迹分析                    processFrame(sampleBuffer, atTime: timestamp, withDuration:frameDuration)                }            }        }    }}
复制代码


processFram 方法进行轨迹分析,实现如下:


func processFrame(_ sampleBuffer: CMSampleBuffer, atTime time : CMTime, withDuration duration : CMTime) {    // 创建句柄    let handler = VNImageRequestHandler(cmSampleBuffer: sampleBuffer, orientation: .up)    // 发起分析请求    try? handler.perform([request])}
复制代码


request 对象的构建如下:


lazy var request: VNDetectTrajectoriesRequest = {    let req = VNDetectTrajectoriesRequest(frameAnalysisSpacing:.zero, trajectoryLength: 10) { result, error in        if let error {            print(error)        }        self.handleResult(request: result as! VNDetectTrajectoriesRequest)    }    return req}()
复制代码


这里的参数后面会详细解释。


在示例中,我们可以添加一个 AVPlayer 来播放原视频,然后将分析出的轨迹绘制到视频对应的位置上进行对比。handleResult 方法示例如下:


func handleResult(request: VNDetectTrajectoriesRequest) {    for res in request.results ?? [] {        // 校正后的轨迹点        let points = res.projectedPoints        for p in points {            DispatchQueue.main.async {                let v = UIView()                // 视频宽高比                let scale = self.image.size.width / self.image.size.height                let width = self.view.frame.width                let height = width / scale                let size = CGSize(width: width, height:height)                v.backgroundColor = .red                // 播放器充满页面,居中播放视频的y轴偏移                let offsetY = self.view.frame.height / 2 - height / 2                v.frame = CGRect(x: p.x * size.width, y: (1 - p.y) * size.height + offsetY, width: 4, height: 4)                self.view.addSubview(v)            }        }    }}
复制代码


轨迹分析效果如下所示:


2 - VNDetectTrajectoriesRequest 与 VNTrajectoryObservation 类

VNDetectTrajectoriesRequest 类一种有状态的分析请求类,继承自 VNStatefulRequest,VNDetectTrajectoriesRequest 定义如下:


open class VNDetectTrajectoriesRequest : VNStatefulRequest {    // 构造方法    // frameAnalysisSpacing参数设置采样间隔      // trajectoryLength设置确定一条轨迹的点数 最小为5    public init(frameAnalysisSpacing: CMTime, trajectoryLength: Int, completionHandler: VNRequestCompletionHandler? = nil)    // 轨迹点数    open var trajectoryLength: Int { get }    // 设置要检测的对象的最小半径    open var objectMinimumNormalizedRadius: Float    open var minimumObjectSize: Float
// 设置要检测对象的最大半径 open var objectMaximumNormalizedRadius: Float open var maximumObjectSize: Float // 检测的目标帧的时间 open var targetFrameTime: CMTime
// 分析结果 open var results: [VNTrajectoryObservation]? { get }}
复制代码


VNTrajectoryObservatio 类是轨迹分析的结果类,其内封装了组成轨迹的点。定义如下:


open class VNTrajectoryObservation : VNObservation {    // 检测出的未处理前的原始点    open var detectedPoints: [VNPoint] { get }    // 矫正后的轨迹点    open var projectedPoints: [VNPoint] { get }    // 描述轨迹的抛物线方程    open var equationCoefficients: simd_float3 { get }    // 测量的物体的半径平均值    open var movingAverageRadius: CGFloat { get }}
复制代码


其中 equationCoefficients 属性是模拟出的抛物线方程,即下面的公式:


y = a*x^2 + b*x + c


simd_float3 结构中会封装 a,b 和 c 的值。

发布于: 刚刚阅读数: 3
用户头像

珲少

关注

还未添加个人签名 2022-07-26 加入

还未添加个人简介

评论

发布
暂无评论
iOS MachineLearning 系列(6)—— 视频中的物体轨迹分析_珲少_InfoQ写作社区