iOS - 苹果的Vision综合实践

iOS11引入的Vision Framework,这几年在不断的添加新能力。今年新增的能力比较多,苹果直接整了一个完整的App和文档,来综合的展示新的视觉能力,文章本身是对这个demo App和文档的解读和翻译,demo app的链接在文末。

丢沙包游戏

丢沙包游戏

这个游戏貌似是老美很常见的一个游戏,主要元素是:

  1. 一个人,几个沙包
  2. 一个带有洞的木板(2*4英尺大小的木板,6英寸直径的洞)
  3. 游戏规则就是人站在25英尺外扔沙包
  4. 碰到木板或者进洞就算得分

Action & Vision

苹果以丢沙包这个游戏为例,写了一个名字叫Action & Vision的App,通过iOS的视觉能力来帮助用户分析游戏行为和统计分数。实际运行效果如下。

Action & Vision

业务流程和代码

  1. Vision基础
    所有Vision能力,命名都是一个VNXXXRequest,比如VNDetectHumanBodyPoseRequest。每当我们需要使用一个能力,我们需要创建一个request,并且使用一个VNImageRequestHandler,来输入图片,以及需要使用的[request]能力数组,最后,我们调用handle.perform(request),来执行实际的预测,并冲request中获取结果,即一个observation.
// 创建request
private let detectPlayerRequest = VNDetectHumanBodyPoseRequest()
// 输入图片并执行request
let visionHandler = VNImageRequestHandler(cmSampleBuffer: buffer, orientation: orientation, options: [:])
// 获取结果
let results = self.detectTrajectoryRequest.results as? [VNTrajectoryObservation] 
  1. 游戏前的定位和分析。
    1.1 首先需要识别木板的位置,苹果使用了Create ML自己创建了一个模型,来识别木板。
    1.2 然后通过VNDetectContoursRequest,来确定木板的边缘,确定像素和实际物体长宽的对应关系,来确定洞在木板和图片中的位置。因为在1.1中已经识别了木板,实际使用VNDetectContoursRequest的过程是,把1.1中识别的木板的bouding box拿到,在使用VNDetectContoursRequest时指定regionOfInterest,减少噪声提高效率。
        let contoursRequest = VNDetectContoursRequest()
        contoursRequest.regionOfInterest = boardBoundingBox.visionRect
  1. 确定画面的稳定性
    之前的普遍做法是,使用陀螺仪,隔一段时间计算陀螺仪的参数diff是否大于一个自己的阈值,来判断用户或者画面是否稳定。现在可以使用苹果提供的新API - VNTranslationalImageRegistrationRequest,用判断两张图仿射变换xy的距离来判断画面的稳定性。

利用VNTranslationalImageRegistrationRequest获取反射变换transform

    private func checkSceneStability(_ controller: CameraViewController, _ buffer: CMSampleBuffer, _ orientation: CGImagePropertyOrientation) throws {
        guard let previousBuffer = self.previousSampleBuffer else {
            self.previousSampleBuffer = buffer
            return
        }
        let registrationRequest = VNTranslationalImageRegistrationRequest(targetedCMSampleBuffer: buffer)
        try sceneStabilityRequestHandler.perform([registrationRequest], on: previousBuffer, orientation: orientation)
        self.previousSampleBuffer = buffer
        if let alignmentObservation = registrationRequest.results?.first as? VNImageTranslationAlignmentObservation {
            let transform = alignmentObservation.alignmentTransform
            sceneStabilityHistoryPoints.append(CGPoint(x: transform.tx, y: transform.ty))
        }
    }

判断transform x,y,确定画面是否稳定

var sceneStability: SceneStabilityResult {
        // Determine if we have enough evidence of stability.
        guard sceneStabilityHistoryPoints.count > sceneStabilityRequiredHistoryLength else {
            return .unknown
        }
        
        // Calculate the moving average by adding up values of stored points
        // returned by VNTranslationalImageRegistrationRequest for both axis
        var movingAverage = CGPoint.zero
        movingAverage.x = sceneStabilityHistoryPoints.map { $0.x }.reduce(.zero, +)
        movingAverage.y = sceneStabilityHistoryPoints.map { $0.y }.reduce(.zero, +)
        // Get the moving distance by adding absolute moving average values of individual axis
        let distance = abs(movingAverage.x) + abs(movingAverage.y)
        // If the distance is not significant enough to affect the game analysis (less that 10 points),
        // we declare the scene being stable
        return (distance < 10 ? .stable : .unstable)
    }
  1. 识别进入画面的玩家(人体识别)
    使用VNDetectHumanBodyPoseRequest来获取人体和姿势数据。
    3.1 根据observation.confidence结果,判断是否有人,根据observation.recognizedPoints,把points的结果合并,返回人的CGRect。
    3.2 转换一下recognizedPoints的格式,根据部分点位,画出人的姿态。


    人的姿态

这个recognizedPoints实际的数据是这样的

left_hand_joint
[0.201963; 0.587417]

然后你需要赛选几个你想要绘制的点,使用bezier path将他们链接,绘制,然后更新他们的位置。

3.3 姿势检测,基本原理是通过create ML训练一个模型来分来。步骤是:获取一张图,获取图中人的姿势结果,也就是通过VNDetectHumanBodyPoseRequest获取到observation,observation中有姿势相关的point,将他们构造一个MLMultiArray作为输入,使用core ML跑模型,来进行姿势分类。

  1. 检测投掷沙包的抛物线
    使用VNDetectTrajectoriesRequest来检测抛物线轨迹。因为抛物线是多个点组成的,所以一个VNDetectTrajectoriesRequest实例需要一系列的视频帧来多次执行,获取到足够多的点,然后执行完成回调。Demo中把这些抛物线的点连成一条曲线,绘制出来。


    抛物线
private lazy var detectTrajectoryRequest: VNDetectTrajectoriesRequest! =
                        VNDetectTrajectoriesRequest(frameAnalysisSpacing: .zero, trajectoryLength: GameConstants.trajectoryLength)
try visionHandler.perform([self.detectTrajectoryRequest])
let results = self.detectTrajectoryRequest.results as? [VNTrajectoryObservation]
let trajectory = UIBezierPath()
        for point in points.dropFirst() {
            trajectory.addLine(to: point.location)
        }
// 然后绘制 draw path

Building a Feature-Rich App for Sports Analysis
丢沙包 - Cornhole

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351