ASCII倶楽部

このページの本文へ

Swift Playgroundsで学ぶiOSプログラミング 第91回

CoreMLの機械学習モデルを活用(その2)

iPadの内蔵カメラで撮影した写真を認識するプログラム

2018年06月13日 17時00分更新

文● 柴田文彦 編集●吉田ヒロ

  • この記事をはてなブックマークに追加
  • 本文印刷

 このところ数回に渡って取り上げてきたCoreMLの仕上げとして、と同時に前回のVisionフレームワークから利用するCoreMLモデルのアプリ化の例として、今回はiPadのカメラで撮影した写真をその場で認識させて結果を表示するプログラムを作ります。

 Visionから利用するCoreMLモデルとしては、前回と同じSqueezeNetを利用します。念のために繰り返すと、このモデルは「木、動物、食べ物、乗り物、人々など、1000のカテゴリセットから画像に存在する主なオブジェクトを検出します。」という機能を持つものの中で最もファイルサイズが小さな(約5MB)ものでした。

 これまでは認識に使用した写真は、あらかじめ用意したものをiCloudドライブからプレイグラウンドに読み込んでいました。今回は、その写真をiPadの内蔵カメラを使ってその場で撮影したものに置き換えることになります。そのためにはこの連載で以前にも利用したように、iPadのカメラ機能にイメージピッカーコントローラー(UIImagePickerController)経由でアクセスすることにします。iPadの内蔵カメラを利用する方法には、もっと低レベルのフレームワークを利用して、直接画像(映像)を取得するものもあります。しかし、イメージピッカーコントローラーを使えば、カメラのユーザーインターフェースなども含めて提供してくれ、撮影した写真画像もUIImageとして簡単に取り出すことができます。今回の目的にはぴったりの手軽な方法です。

 今回のプログラムを試してみて期待したような認識率が得られないと感じたら、アップルがサンプルとして提供しているほかのCoreMLモデルも試してみるといいでしょう。現在のiPadであれば、モデルのファイルサイズはそれほど問題になるほどの大きさではありません。ただし、モデルをSwiftから利用できるようにするクラス定義のソースコードは、Xcodeを利用して取得する必要があります。その部分だけは、Macを使用しなければならないのが、ちょっと残念な点です。

Swift Playgrounds 2.1の実行環境の不可解な挙動について

 今回の本題に入る前に、現在最新のSwift Playgrounds 2.1のプログラム実行環境について簡単に確認しておきましょう。実はバージョンが2.1になってから、これまでこの連載で紹介したプログラムのコードが動かなくなる例が出てくるようになりました。それにはいろいろな理由が考えられますが、最も影響が大きいのは、2.1が提供するiOSアプリの実行環境のビューの扱いの違いです。特に、ビューコントローラーに最初から含まれているビューの挙動が、これまでのバージョンとは違っています。その結果、いままでは何の問題もなく動いていたプログラムが、意外なところで実行時エラーを発生するようになっています。

 例えば、ビューコントローラーのviewDidLoadメソッドの中で、そのコントローラーに固有のビュー(self.view)の背景色を変えようとするだけでエラーが発生します。また、そのビューに、新たに作成した別のビューをaddSubviewしようとすると、やはりエラーになります。このようなエラーを回避する方法も何とおりか考えられます。1つは、viewDidLoadの中で新たにビュー(UIView)のオブジェクトを作成し、コントローラー固有のビューを、その新たなビューで置き換えてから作業するという方法です。そこまでしなくても、コントローラーのビューに何らかの変更を加える前にそのframeサイズをゼロに設定しておく方法もあります。理由ははっきりしませんが、それだけで、その後の実行エラーを回避できます。

 このようエラー対策が必要となったのは、バージョン2.1の不具合なのか、あるいは新たな実行環境の仕様なのか、今のところ判断が難しいところです。いずれにせよ、ここではどちらなのか判断するよりも、プログラムを動かすことの方が大事なので、ここしばらくは回避策を入れつつ、だましだまし使うことにしたいと思います。

写真撮影アプリをSwift Playgrounds 2.1に対応させる

 前置きが長くなりましたが、ここからが本題です。まずは、CoreMLのことは忘れて、写真撮影プログラムを動かしてみましょう。このプログラムは以前(第59回、第60回)で作成したものとほとんど同じです。第59回ではイメージピッカーコントローラーを使った写真撮影を実現し、第60回では同様にして撮影した写真にCore Imageによる画像フィルターをかけたのでした。

 今回の最終的なプログラムとしては、第60回のCore Imageフィルターの部分をCoreMLによる認識に置き換えたようなかたちになります。まずは、第59回と同様の写真撮影の部分のみを動かします。

 プログラムの流れについては第59回の本文も参照してください。今回は異なる部分を中心にざっと解説します。まず、撮影した画像を表示するイメージビューと再度撮影するためのボタンまでは同じですが、今回は認識結果の物の名前と確度を表示する2つのラベルをあらかじめ用意しています。

まずは、以前にも利用したことがあるイメージピッカーコントローラーを使って、iPadの内蔵カメラで写真撮影するプログラムを作ります。ビューコントローラーは、UIImagePickerControllerDelegateとUINavigationControllerDelegateの両プロトコルに準拠させる必要があります

 またviewDidLoadの中では上で説明したように、ビューコントローラーのビューに変更を加える前にそのフレームサイズをゼロに設定しています。こうしておけば、とりあえずaddSubviewで実行エラーになることもありません。元からあるビューの上に配置するのは、イメージビューのほか1つのボタン(UIButton)と2つのラベル(UILabel)です。

ビューコントローラーのビューには、撮影した画像を表示するイメージビューのほか、実際に撮影するためのボタン、結果を表示する2つのラベルもサブビューとして付加しておきます

 viewWillLayoutSubviewsメソッドでは、例によって元のビューの上に配置したサブビューの位置を決めていきます。設定の順番で言えば、上からイメージビュー、ボタン、そして2つのラベルとなります。

いつものようにビューの上に配置したサブビューのレイアウトは、viewWillLayoutSubviewsメソッドの中で設定します

 イメージピッカーコントローラーのデリゲートとして必要なdidFinishPickingMediaWithInfoのメソッドは、イメージピッカーを使って写真を取り終わったときに呼ばれます。ここでは、とりあえず撮影した画像をイメージビューにはめ込み、2つのラベルにそれぞれ「Nothing」と「1.0」という固定の文字列を書き込んでから、dismissメソッドで、イメージピッカーのビューを閉じています。

実際に写真撮影に関わる2つのメソッドです。1つは、イメージピッカーによる写真撮影が終わってユーザーが「写真を使用」ボタンにタップした直後に呼ばれるdidFinishPickingMediaWithInfoのメソッド。もう1つは、イメージピッカーによる写真撮影機能を呼び出して画面を切り替えるためのtakePhotoです

 もう1つ、「再認識」というタイトルを付けて配置したボタンをタップすると呼ばれるのがtakePhotoメソッドです。ここでは、カメラによる撮影を指定してイメージピッカーを開くだけのものです。このメソッドは、プログラムを起動した直後にも、何も操作をしなくても呼ばれるようになっています。

 このプログラムを起動すると、最初にイメージピッカーのビューが表示され、写真撮影の画面となります。

プログラムを起動すると、いきなり写真撮影が始まります。適当な物体にiPadの内蔵カメラを向けて丸いシャッターボタンをタップしてみましょう

 丸い大きなシャッターボタンをタップすると、そのとき表示している画像を撮影し、静止画を表示します。その際に、画面下部の左側には「再撮影」、右側には「写真を使用」というボタンが表示されます。これらはイメージピッカーが表示しているものです。

シャッターボタンを押すと、とりあえず撮影が完了します。ここで「際撮影」をタップすれば、再び撮影モードとなります。「写真を使用」をタップすれば、表示されている写真が「確定」します

 「写真を使用」をタップすれば、イメージピッカーによる撮影が完了し、didFinishPickingMediaWithInfoのメソッドが呼ばれるので、上で説明したような結果となります。

プログラムにはまだ認識機能は組み込んでいません。撮影した写真がそのまま表示され、結果のラベルには「Nothing」と「1.0」が表示されるだけです

 ここで「再認識」ボタンをタップすると、再び写真撮影をやり直すことができます。

カテゴリートップへ

この連載の記事

ASCII倶楽部の新着記事

会員専用動画の紹介も!