Swift初心者がCoreMLで機械学習に足を踏み入れてみた

# はじめに
Swift初心者ではありますが、以前から興味があったCoreMLで機械学習にチャレンジしてみました。
機械学習と聞くと、なんとなく難しそうでハードルが高いイメージがあったのですが、実際に触れてみると想像よりも簡単に実装できましたのでまとめてみました。

そもそもCore MLとは？については、まとめられている記事がありましたので紹介させていだきます。
[Appleの機械学習がヤバい](https://qiita.com/cthxn77r/items/b6727e6731fe71e2c52a)

# 成果物
今回は、機械学習モデルを活用してiPhoneで撮影した画像の中の主要なオブジェクトを分類して、その中で１番割合を占めているオブジェクト名を表示する機能を作成していきます。
![coremltest.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416607/582cc894-d468-5260-bef3-1ed7b1f16e26.gif)

Navigation barに`Siamese cat`と表示されました。
`Siamese cat = シャム猫`のようです。（正解はラグドールなのでOKですかね？笑）
猫の種類まで検出するのすごい。。
一応猫を判別することに成功しました！


## 作成の流れ
作成にあたり以下のような流れで進めていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416607/a7e8e6d2-088e-06b2-cc52-6423fcdf8603.png)


# 1. Core MLを使用するための前準備
`Core ML`を使用するためには、モデルのダウンロードとインポートが必要になります。
ここでは、こちらの工程を説明していきます。

## 1-1. Core MLモデルのダウンロード
今回は、画像識別を行うためのCore MLモデルの1つである`MobileNetV2`を使用します。
そのために、以下Apple developerサイトのモデル一覧からダウンロードする必要があります。
https://developer.apple.com/jp/machine-learning/models/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416607/e631a3cd-83e2-dc7a-ae4f-3a8b722a6458.png)
「モデルを見る」をクリックすると以下の画像が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416607/e6a1c0ef-58c7-ba35-f12c-6672b512e33f.png)
赤枠内のファイルをダウンロードすることで、今回使用する`Core MLモデル`を入手することができます。

## 1-2. Core MLモデルをインポート
実際に使用するためにXcodeに`Core MLモデル`をインポートしていきます。
とは言っても、実際には`ドラッグ&ドロップ`するだけの作業になります。。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416607/fffe69db-2bb6-6dca-5642-d111acc001ce.png)

`MobileNetV2.mlmodel`が追加されていれば準備作業は完了です。
続いて、実装作業に進んでいきます。

# 2. 実装
ここでは、
**2-1. iPhoneで撮影した画像を表示する処理**
**2-2. 画像データを機械学習モデルで分類する処理**
を順に実装していきます。

## 2-1. iPhoneで撮影した画像を表示する処理

### 2-1-1. StoryBoadに設置
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416607/843a52db-5906-3c84-8ee2-55486d4c13a7.png)
今回は個人的に楽に実装しやすそうな`NavigationBar`に`Camera`を配置していきます。
撮影した写真を表示させるために`UIImageView`を配置します。

### 2-1-2. 機能を実装
先ほど設置したオブジェクトにアクションを追加していきます。

- `Camera`をタップするとカメラを起動させる
- `UIImageView`に撮影した画像を表示させる

```swift:ViewController.swift
class ViewController: UIViewController, UINavigationControllerDelegate {
    // 撮影した画像を表示するImageView
    @IBOutlet weak var imageView: UIImageView!
    
    let imagePicker = UIImagePickerController()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        imagePicker.delegate = self
        // .photoLibraryにするとフォト内の写真を開くことができます。
        imagePicker.sourceType = .camera
        imagePicker.allowsEditing = false
        
    }
    /// NavigationbarItemのカメラをタップした時のアクション
    @IBAction func didSelectCamera(_ sender: Any) {
        present(imagePicker, animated: true, completion: nil)
    }
}

/// UIImagePickerの処理
extension ViewController: UIImagePickerControllerDelegate {
    
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        
        if let userSelectedImage = info[.originalImage] as? UIImage {
            imageView.image = userSelectedImage
        }
        imagePicker.dismiss(animated: true, completion: nil)
    }
}
```

>**UIImagePickerController**:
>https://developer.apple.com/documentation/uikit/uiimagepickercontroller


###2-1-3. info.plistでカメラ使用許可などを設定

カメラ使用などにユーザーの許可が必要になるためinfo.plistに設定を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416607/cc4bb839-18fa-d718-e50c-623f5eb49e11.png)

コードではこちらになります。

```swift:info.plist
<key>NSPhotoLibraryUsageDescription</key>
<string>こちらの機能にはフォトを使用します</string>
<key>NSCameraUsageDescription</key>
<string>こちらの機能にはカメラを使用します</string>
```

## 2-2. 機械学習の処理を実装
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416607/8b9d3624-0888-4a7b-661b-c421e79af723.png)
今回は、Core MLで画像の分類を行うためVisionフレームワークを使用して実装していきます。

### 2-2-1. 使用するフレームワークなどをimportする
```swift:ViewController.swift
import CoreML
import Vision
```

>**Core ML**:
>https://developer.apple.com/jp/documentation/coreml
>**Visionフレームワーク**: 
>https://developer.apple.com/documentation/vision

### 2-2-2. Core MLモデルのインスタンス生成と処理

```swift:ViewController.swift
// 使用するCore MLモデルのインスタンスを生成
let model = VNCoreMLModel(for: MobileNetV2().model)
// これでVisionからリクエストを送りmodelを元に解析することができる
let request = VNCoreMLRequest(model: model) { (request, error) in
     // 解析結果を分類情報として保存
     guard let results = request.results as? [VNClassificationObservation] else {
         return
     }
}
```

>**VNCoreMLModel**:
>https://developer.apple.com/documentation/vision/vncoremlmodel
>**VNClassificationObservation**:
>https://developer.apple.com/documentation/vision/vnclassificationobservation

### 2-2-3. 画像の解析リクエストをする
2-2-2で作成したCore MLモデルの解析をリクエストする処理を書いていきます。

```swift:ViewController.swift
// Visionへリクエストを送るためにUIImage→CIImageへ変換する
let ciImage = CIImage(image: image)

// 画像解析をリクエスト
let handler = VNImageRequestHandler(ciImage: ciImage)

// リクエストを実行
do {
    try handler.perform([request])
}
catch {
    print(error)
}
```

>**CIImage**:
>https://developer.apple.com/documentation/coreimage/ciimage
>**VNImageRequestHandler**:
>https://developer.apple.com/documentation/vision/vnimagerequesthandler


# 3. 完成形

```swift:ViewController.swift
import UIKit
import CoreML
import Vision

class ViewController: UIViewController {
    // 撮影した画像を表示するImageView
    @IBOutlet weak var imageView: UIImageView!
    
    let imagePicker = UIImagePickerController()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        imagePicker.delegate = self
        imagePicker.sourceType = .camera
        imagePicker.allowsEditing = false
        
    }
    
    @IBAction func didSelectCamera(_ sender: Any) {
        present(imagePicker, animated: true, completion: nil)
    }
    
}

/// UIImagePickerの処理
extension ViewController: UINavigationControllerDelegate, UIImagePickerControllerDelegate {
    
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        
        if let userSelectedImage = info[.originalImage] as? UIImage {
            imageView.image = userSelectedImage
            
            // 画像からオブジェクトを検出し、出力する
            detectImageObject(image: userSelectedImage)
        }
        
        imagePicker.dismiss(animated: true, completion: nil)
    }
    
    /// 画像からオブジェクトを検出・結果を出力
    func detectImageObject(image: UIImage) {
        // VNCoreMLModel(for: xx.modle): xxは使用するCore MLモデルによって変わります
        guard let ciImage = CIImage(image: image), let model = try? VNCoreMLModel(for: MobileNetV2().model) else {
            return
        }
        // Core MLモデルを使用して画像を処理する画像解析リクエスト
        let request = VNCoreMLRequest(model: model) { (request, error) in
            // 解析結果を分類情報として保存
            guard let results = request.results as? [VNClassificationObservation] else {
                return
            }
            
            // 画像内の一番割合が大きいオブジェクトを出力する
            if let firstResult = results.first {
                let objectArray = firstResult.identifier.components(separatedBy: ",")
                if objectArray.count == 1 {
                    self.navigationItem.title = firstResult.identifier
                } else {
                    self.navigationItem.title = objectArray.first
                }
            }
        }

        // 画像解析をリクエスト
        let handler = VNImageRequestHandler(ciImage: ciImage)
        
        // リクエストを実行
        do {
            try handler.perform([request])
        }
        catch {
            print(error)
        }
    }
}

```
>**VisionおよびCore MLを使用した画像の分類**
>https://developer.apple.com/documentation/vision/classifying_images_with_vision_and_core_ml

# まとめ

Core MLの情報はdeveloper.appleにサンプルコードなどがあり、手が出しやすい部類かなと思いました。また、用意されているCore MLモデル以外に他社製の機械学習モデルも変換してアプリで使用することができるようなので、かなりの可能性が秘めているなと感じました。ただ、まだ理解できてないところが多いので、実際にコードを書きながら理解を深めて記事でも書ければと思います。

また、本記事に間違い等ございましたら、
大変恐縮でありますがご指摘いただければと存じます。
よろしくお願いいたします。