iOS 11 SDKの新機能――ARアプリが作れるARKit、機械学習済みのモデルが使えるCore MLを試してみた
iOS 11でも開発者向けにも多くの面白い機能が追加された。本稿では、そのうちの「ARKit」「Core ML」について、実際にアプリを作りながら紹介する。
2017年9月19日(米国時間)、iOS 11の正式版がリリースされました。iOS 11では、ユーザー向けだけではなく開発者向けにも多くの面白い機能が追加されています。本稿では、そのうちの「ARKit」「Core ML」について、実際にアプリを作りながら紹介します。
ARKitとは
ARKitとはAR(Augmented Reality)アプリの開発をサポートしてくれるフレームワークです。
ARとは現実の映像にオブジェクトや情報を重ねる技術で、ゲームや医療、教育など多くの分野で利用されています。
ARKitでは、それらを実現するための、現実の映像へのオブジェクトの重ね合わせ、水平な面の検出、映像内の距離計算などの機能を提供しています。
今回は下記の要件を満たすアプリを作りながらARKitの概要を紹介します。
- アプリを立ち上げるとカメラが起動する
- カメラ越しの床の上に、オブジェクトを配置する
- そのオブジェクトをタップすると回転する
ARKitのサンプルコードはこちらからダウンロードできます。
サンプルアプリを動かしてみる
まずは、ARKitのサンプルアプリを動かしてみます。
Xcodeを立ち上げて「Create a New Xcode project」からプロジェクトを作成します。プロジェクトの種類は「Augmented Reality App」を選択します。
アプリ名・チーム名などの情報を入力します。「Content Technology」は「SceneKit」を選びます。
SceneKitはAppleが提供する3Dゲーム開発などに使われるフレームワークです。今回は3Dのオブジェクトを配置するので「SceneKit」を選択します。
プロジェクトを作ったらアプリを起動します。起動すると、下記のように空中に飛行機が浮いているような画面になります。
なお、ARKitはシミュレーターでは動かせないので、実機での起動が必要です。
ARKitでは端末の傾きや移動に応じてオブジェクトも動きます。下記は端末の角度を変えたときのスクリーンショットです。端末を斜めにすることで、飛行機の角度と位置が変化したことが分かると思います。
床にオブジェクトを配置する
続けて、先ほど要件を決めたアプリを作ってみます。
まずは、サンプルアプリから不要な処理を取り除きます。「ViewController.swift」を下記のように修正してください。
import UIKit import SceneKit import ARKit class ViewController: UIViewController, ARSCNViewDelegate { @IBOutlet var sceneView: ARSCNView! override func viewDidLoad() { super.viewDidLoad() sceneView.delegate = self sceneView.debugOptions = ARSCNDebugOptions.showFeaturePoints sceneView.scene = SCNScene() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) let configuration = ARWorldTrackingConfiguration() sceneView.session.run(configuration) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) sceneView.session.pause() } }
これで、カメラが立ち上がるだけのアプリになりました。
続けて、床を検知する機能を追加します。「ViewController」を下記のように修正してください。
class ViewController: UIViewController, ARSCNViewDelegate { // 省略 override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) let configuration = ARWorldTrackingConfiguration() configuration.planeDetection = .horizontal // 今回追加 sceneView.session.run(configuration) } // 省略 func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { // 水平な面を検知したときに呼ばれる } }
ここでは、「planeDetection」の設定と「renderer」メソッドの定義を行いました。「planeDetection」は検知対象を設定するプロパティーです、今のところ「.horizontal」(水平方向)のみ対応しています。
水平な面を検知した際には、先ほど定義した「renderer」メソッドが呼ばれます。このメソッドはARSCNViewのdelegateメソッドになります。
最後に床の上にオブジェクトを配置する処理を追加します。「ViewController」の「renderer」メソッドを下記のように修正してください。
class ViewController: UIViewController, ARSCNViewDelegate { // 省略 func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { guard let planeAnchor = anchor as? ARPlaneAnchor else { return } let box = SCNBox(width: 0.1, height: 0.1, length: 0.05, chamferRadius: 0.01) let planeNode = SCNNode(geometry: box) planeNode.position = SCNVector3Make(planeAnchor.center.x, 0, planeAnchor.center.z) planeNode.transform = SCNMatrix4MakeRotation(-Float.pi / 2, 1, 0, 0) node.addChildNode(planeNode) let material = SCNMaterial() material.diffuse.contents = UIColor(red: 0.85, green: 0.95, blue: 0.85, alpha:1.00) let sideMaterial = SCNMaterial() sideMaterial.diffuse.contents = UIColor(red: 0.55, green: 0.57, blue: 0.55, alpha: 1.00) planeNode.geometry?.materials = [material, sideMaterial] } }
アプリを起動してカメラを床に向けて待つと、オブジェクトが配置されます。
タップイベントを取得する
最後にオブジェクトをタップすると回転する処理を実装します。
まずは「UITapGestureRecognizer」を使ってタップイベントを取得できるようにします。「ViewController」を下記のように修正してください。
class ViewController: UIViewController, ARSCNViewDelegate { @IBOutlet var sceneView: ARSCNView! override func viewDidLoad() { // 省略 sceneView.addGestureRecognizer(UITapGestureRecognizer( target: self, action: #selector(self.tapView(sender:)))) } // 省略 @objc func tapView(sender: UITapGestureRecognizer) { } }
タップされたときにオブジェクトが回転する処理を追加します。先ほど定義したtapViewを下記のように修正してください。
class ViewController: UIViewController, ARSCNViewDelegate { // 省略 @objc func tapView(sender: UITapGestureRecognizer) { let tapPoint = sender.location(in: sceneView) let results = sceneView.hitTest(tapPoint, types: .existingPlaneUsingExtent) results.forEach { guard let anchor = $0.anchor else { return } let node = sceneView.node(for: anchor) let action = SCNAction.rotateBy(x: 10, y: 0, z: 0, duration: 2.0) node?.childNodes.first?.runAction(action) } } }
これでタップ時にオブジェクトが回転するようになりました。
タップした場所にオブジェクトがあるかどうかは「hitTest」メソッドで判定しています。このメソッドは、タップした場所と重なる水平な面を返してくれます。この機能を使ってタップした場所にあるオブジェクトを回転させる処理を実装しました。
Core MLとは
続いて、Core MLについて見ていきます。
Core MLは機械学習に関するフレームワークで、学習済みのモデルを使った推論を行うための機能を備えています。今までも学習済みモデルを使った推論は可能でしたが、今回追加されたCore MLによって、それが簡単に行えるようになりました。
Core MLとVision
Core MLは単体で使うことも可能ですが、他フレームワークとの併用もサポートされています。具体的には画像解析には「Vision」、ゲームに利用する場合は「GameplayKit」が使えます。それぞれのフレームワークにはCore MLを使うためのAPIが用意されており、それらを活用することでCore MLをより簡単に扱えます。
今回は画像を使ったアプリを作るため、Visionを利用します。
CoreMLのサンプルコードはこちらからダウンロードできます。
画像選択機能の実装
それでは、Core MLを使ったアプリを作っていきます。
今回はCore MLを使って、写真に写っているものを解析するアプリ、具体的には下記のような要件のアプリを作ります。
- 端末内の画像を選択できる
- 画像を選択すると、その画像に何が写っているかを推測する
まずはプロジェクトを作成します。今回は「Single View App」を作成します。
続けて画像選択処理を実装します。作成したプロジェクトの「ViewController.swift」を下記のように修正してください。
import UIKit class ViewController: UIViewController { let imageView = UIImageView() override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = UIColor(red: 0.98, green: 0.98, blue: 0.98, alpha: 1) imageView.backgroundColor = UIColor.gray imageView.frame = CGRect(x: (view.frame.width - 240) / 2, y: 20, width: 240, height: 240) view.addSubview(imageView) let selectBtn = UIButton(frame: CGRect(x: 0, y: view.frame.height - 44, width: view.frame.width, height: 44)) selectBtn.addTarget(self, action: #selector(self.tapSelectBtn), for: .touchUpInside) selectBtn.setTitle("画像選択", for: .normal) selectBtn.setTitleColor(.white, for: .normal) selectBtn.backgroundColor = .darkGray view.addSubview(selectBtn) } @objc func tapSelectBtn() { let c = UIImagePickerController() c.delegate = self present(c, animated: true) } } extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { if let image = info[UIImagePickerControllerOriginalImage] as? UIImage { imageView.image = image picker.dismiss(animated: true, completion: nil) } } func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { picker.dismiss(animated: true, completion: nil) } }
これで選択された画像と画像選択ボタンのみのアプリができました。
画像選択ボタンを押すと、下記のように画像選択画面が表示されます。
画像の解析機能
続けて、選択した画像に何が写っているかを推測する処理を追加します。今回はAppleの用意している学習済みモデルを利用して推測します。
「Download Core ML Model」から「MobileNet.mlmodel」をダウンロードしてください。ダウンロードが完了したら、ドラッグ&ドロップでプロジェクトに追加します。
もし「Target Membership」にチェックが入っていない場合は、チェックを入れるようにしてください。
最後に、推論処理を実装します。「ViewController」を下記のように修正してください。
import UIKit import CoreML // 今回追加 import Vision // 今回追加 class ViewController: UIViewController { let imageView = UIImageView() let resultLabel = UILabel() // 今回追加 override func viewDidLoad() { // 省略 resultLabel.frame = CGRect(x: (view.frame.width - 240) / 2, y: imageView.frame.maxY, width: 240, height: 200) resultLabel.numberOfLines = 0 view.addSubview(resultLabel) } // 省略 } extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { if let image = info[UIImagePickerControllerOriginalImage] as? UIImage, let model = try? VNCoreMLModel(for: MobileNet().model) { imageView.image = image picker.dismiss(animated: true, completion: nil) let request = VNCoreMLRequest(model: model) { request, error in guard let results = request.results as? [VNClassificationObservation] else { return } let resultStrs = results.map { "\($0.identifier): \(NSString(format: "%.2f", $0.confidence))" }.prefix(5) self.resultLabel.text = "● 解析結果\n" + resultStrs.joined(separator: "\n") } request.imageCropAndScaleOption = .centerCrop let handler = VNImageRequestHandler(cgImage: image.cgImage!) try! handler.perform([request]) } } func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { picker.dismiss(animated: true, completion: nil) } }
実行結果
起動して画像を選択すると、画像に写っているものを推論してくれました。何枚か試したところ、高い精度で認識してくれました。
なお、Core MLのサンプルで使った画像は「フリー素材ぱくたそ」のものを利用しています。
本稿では、iOS 11 SDKの新機能を紹介しましたが、いかがでしたでしょうか。本稿が皆さまの開発の一助になりましたら幸いです。
Copyright © ITmedia, Inc. All Rights Reserved.
関連記事
- iOS 9の最新機能で自動ルート検索を簡単にゲームに組み込む
iPhoneゲームをSwift言語で作成してみたいという初心者向けにiOSのゲームフレームワークを使った作り方を一から解説する入門連載。今回は、敵の動きを改善する。GameplayKitのPathfindingを使ったルートの自動探索の使い方についてSpriteKitのSKActionでの実装と比べて解説。 - iOS 10 SDKの新機能SiriKit、音声認識、iMessage拡張を自作アプリに生かすには
iOS 10で開発者にAPIが解放されたと話題の音声アシスタント「Siri」。その実態はどんなものなのか。SiriKit、Speech/Messages Frameworkの使い方と併せて、簡単なアプリを作りながら解説します。 - スマホ世代でも分かるMacの基本的な使い方&Xcodeをインストールする手順
本連載では、これからプログラミングやiPhoneアプリ開発を始めてみたい方を対象に、開発に必要な知識を基礎から解説していきます。今回は、プログラミングを学び始める前に、まずはMacの基本的な使い方を学び、Xcodeをインストールし、Playgroundを起動してみましょう。