CreateMLとRoboflow Universeで画像認識アプリを作ろう！

# はじめに

CreateMLとRoboflow、SwiftUIという3種の神器を使うことによって、オリジナルAIと画像認識アプリを作ることができます。

# 完成物

今回は、画像に写っているトマトを検出し、それが腐っているかどうか判別するアプリを作りましょう。

![IMG_8838.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823873/b1d7aa85-3d42-7cbb-3e16-4a585983831f.png)

# Roboflow Universeからデータセットをダウンロードしよう

CreateMLでAIモデルを作るには、データのディレクトリ構造をCreateMLが読み取れる形にする必要があります。簡単にCreateML形式でデータセットをダウンロードできる**Roboflow Universe**を使いましょう。

まずはこのサイトにアクセスしましょう。

https://universe.roboflow.com/

右上から`Create an Account`をしましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823873/505c4e04-f6cb-09f8-633c-c2aeb3c4aaba.png)

中央から欲しいデータセットを調べます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823873/972bc124-772e-b9a6-fc80-441f7caff917.png)

今回は腐ったトマトなので`rotten tomato`など調べてみましょう。

https://universe.roboflow.com/yzuproject/expired_tomato

今回はこれを使っていきます。

左から`Dataset`を選択します。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823873/a9779c80-54db-cfcf-40e5-5b33bead9970.png)

右にある`CreateML JSON`をクリックしましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823873/bb424fd1-7848-4d28-acc2-4c981270477a.png)

`Download zip to computer`を選択し、`Continue`をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823873/350cab52-c6cc-2200-4ade-fbcbeca770db.png)

解凍するとこのようなフォルダが出てきます。

![スクリーンショット 2024-12-06 14.25.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823873/d6b082d0-c9f7-300b-3fcd-6de9a0711452.png)

# CreateMLでモデルを学習させよう

## プロジェクトの作成

Xcodeを開き、`Xcode > Open Developer Tool > Create ML`と選びます。

![スクリーンショット 2024-12-06 14.27.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823873/9c944925-0a5d-9f07-f9b2-0e55ae05ee82.png)

左下の`New Document`を選びます。

![スクリーンショット 2024-12-07 11.38.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823873/d70fdf89-0193-b9ff-57dd-e338986192d5.png)

今回は`Object Detection`を選びましょう。

![スクリーンショット 2024-12-07 11.39.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823873/324cb0b9-bf8a-fa1d-5160-3ee46c7bfcb9.png)

プロジェクト名を入力します。

![スクリーンショット 2024-12-07 11.39.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823873/9f245bfa-db1b-9619-78e9-1636f13074e8.png)

右下から`Create`を選択しましょう。

![スクリーンショット 2024-12-07 11.39.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823873/2ed9a826-f6f1-1ede-2b3a-9cbfe7c333c6.png)

このような画面が出てきたら成功です！

![スクリーンショット 2024-12-07 11.39.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823873/fa4e5d1b-82c5-ad01-093a-8d556365fa04.png)

## モデルの学習

`Training Data`のプラスボタンを押しましょう。

![スクリーンショット 2024-12-07 11.39.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823873/f23d319e-f4eb-6b2e-b940-8e7af7a7ec05.png)

先ほどダウンロードしたデータの中の`train`フォルダを選択します。

![スクリーンショット 2024-12-07 11.40.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823873/ae60b744-ce06-f338-eb61-549642aa96d8.png)

このように表示されたら、左上の`Train`をクリックしましょう！
（右にあるVaridationやTestingも追加することは可能ですが、追加しなかった場合は自動でテストを行なってくれます）

![スクリーンショット 2024-12-07 11.48.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823873/81d69d6f-6268-343d-da49-4c51b19d0935.png)

学習中はこのような画面が表示されます。（かなり時間がかかるかもしれません）

![スクリーンショット 2024-12-09 13.55.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823873/113fb34c-3891-b38d-113b-b33a4a0c2f20.png)

時間がかかりすぎる場合はIterationを下げましょう。

学習が終わったら、`Output`を選び、右上の`Get`をクリックしましょう！

![スクリーンショット 2024-12-09 13.57.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823873/42c6520f-be96-7fac-a275-aafff4aa6ed2.png)

保存できたら、このような`mlpackage`が生成されます。

![スクリーンショット 2024-12-09 13.58.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823873/a1109731-22fb-af65-4a90-f7aecaf47a29.png)

# アプリを作成しよう

Xcodeで新しいプロジェクトを作成しましょう。

先ほど作成した`mlpackage`をドラッグ&ドロップでXcodeに追加しましょう。

![スクリーンショット 2024-12-09 23.48.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823873/d516b0a4-c835-f101-c407-29277888d322.png)

`ContentView.swift`を以下のように書き換えていきます。

```swift:ContentView
import SwiftUI
import PhotosUI
import CoreML
import Vision

struct ContentView: View {
    @State private var image: UIImage?
    @State private var results: [TomatoDetectionResult] = []
    @State private var showImagePicker = false
    
    var body: some View {
        VStack {
            if let image = image {
                Image(uiImage: image)
                    .resizable()
                    .scaledToFit()
                    .frame(height: 300)
            } else {
                Text("アルバムから写真を選択してください")
                    .padding()
            }
            
            Button("アルバムを開く") {
                showImagePicker = true
            }
            .padding()
            .sheet(isPresented: $showImagePicker) {
                ImagePicker(image: $image) { newImage in
                    self.image = newImage
                    detectTomatoes(in: newImage)
                }
            }
            
            List(results) { result in
                VStack(alignment: .leading) {
                    Text(result.isFresh ? "新鮮" : "腐っている")
                        .font(.headline)
                    Text("信頼度: \(String(format: "%.2f", result.confidence * 100))%")
                        .font(.subheadline)
                }
            }
        }
    }
    
    private func detectTomatoes(in image: UIImage) {
        guard let model = try? VNCoreMLModel(for: RottenTomatoDetector().model) else { return }
        
        let request = VNCoreMLRequest(model: model) { request, _ in
            guard let results = request.results as? [VNRecognizedObjectObservation] else { return }
            
            self.results = results.map { observation in
                let confidence = observation.confidence
                let label = observation.labels.first?.identifier ?? "未知"
                let isFresh = (label == "fresh")
                
                return TomatoDetectionResult(isFresh: isFresh, confidence: confidence)
            }
        }
        
        if let ciImage = CIImage(image: image) {
            let handler = VNImageRequestHandler(ciImage: ciImage, options: [:])
            try? handler.perform([request])
        }
    }
}

struct TomatoDetectionResult: Identifiable {
    let id = UUID()
    let isFresh: Bool
    let confidence: Float
}
```

ただし、以下の部分は、上で作成した`mlpackage`の名前に揃えるようにしてください。
```swift
guard let model = try? VNCoreMLModel(for: RottenTomatoDetector().model) else { return }
                                          ↑↑ここの部分↑↑
```

実行すれば、目標だったアプリが完成します！

![IMG_8838.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2823873/b1d7aa85-3d42-7cbb-3e16-4a585983831f.png)

# その他のデータセット入手方法

代表的なものが**Kaggle**です。

https://www.kaggle.com/

ここから入手したデータセットは必ずしもCreateMLの形式に準拠してるとは限らないため、データセットのディレクトリ構造を調節する、という手順が必要になることがあります。

# まとめ
AIをつくる、だったり、画像分類アプリを作る、というのは一見難しそうでこのように基礎は簡単に作れるものです。
個人的にはハッカソンなど短期の大会で力を発揮すると考えているので、ぜひ使っていきましょう！