【Swift/iOS】Core MLの実装方法!機械学習で画像解析

【Swift/iOS】Core MLの実装方法!機械学習で画像解析

この記事からわかること

  • Swift/XcodeCore ML使い方
  • 機械学習画像解析する方法
  • イラストへの変換オブジェクト文字識別

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

環境

Core MLとは?

公式リファレンス:Core ML

Core ML」はAppleが提供する機械学習を実装できるフレームワークです。Core MLを使用することでさまざまな機械学習モデル(画像分類、テキスト分類、音声認識など)をiOSアプリに統合することができるようになります。

できること

機械学習モデル:.mlmodel

実装するには機械学習モデルをXcodeプロジェクトに導入します。プロジェクト内に機械学習モデルを持つのでクラウドを介すことなくローカルで処理を完結できるのでセキュリティ的にも安全になっています。

機械学習モデルはCore MLフォーマット(拡張子:.mlmodel)として変換されたものをCore MLでは扱うことが可能です。

Appleからいくつか機械学習モデルが提供されています。今回は「線画分類:MNIST」を使用して0〜9までの手書きの数字を識別するモデルを例にCore MLの実装方法を見ていきます。

【Swift/iOS】Core MLの実装方法!機械学習で画像解析

公式リファレンス:Core MLモデル

実装方法

手書き文字を識別できるモデルですが、Core MLの実装にフォーカスを当てたいため、今回は手書き部分の実装などはなく数字の画像で試します。

  1. .mlmodelファイルの導入
  2. VNCoreMLRequestの作成と実行

1..mlmodelファイルの導入

まずはApple公式のCore MLモデルから「線画分類:MNIST(MNISTClassifier.mlmodel)」をダウンロードします。ダウンロードできたらXcodeにドラッグ&ドロップで入れ込みます。

【Swift/iOS】Core MLの実装方法!機械学習で画像解析

入れ込んだ後に「Model Class」の下に「Automatically generated Swift model class」と出ていれば読み込み完了です。別の言葉が出ていれば「⌘ + B」でビルドすれば読み込みが完了すると思います。それでもダメならClean Buildしてみてください。

ちなみに「Preview」タブでは実際に画像の解析結果を試すことができます。今回は線画分類なので以下のように8と書かれた画像を渡すと「8」が「100% confidence」になっていることを確認できます。

【Swift/iOS】Core MLの実装方法!機械学習で画像解析

2.VNCoreMLRequestの作成と実行

読み込みが完了したらモデルをインスタンス化してVNCoreMLRequestに渡します。そこまでのコードを1行ずつみていきます。

VNCoreMLModelの引数に読み込んだモデルクラスを指定します。先ほどの手順で読み込みが完了していれば該当クラスが自動で生成されているはずです。

guard let model = try? VNCoreMLModel(for: MNISTClassifier(configuration: MLModelConfiguration()).model) else { return }

モデルがインスタンス化できたらVNCoreMLRequestの引数に渡します。completionHandlerから実行した際の結果を参照することができVNClassificationObservation.identifierに今回で言うと解析結果の文字が格納されています。

// VNCoreMLRequestの作成
let request = VNCoreMLRequest(model: model) { request, error in
    if let error = error {
        print("Error: \(error.localizedDescription)")
        return
    }
    // 結果を参照
    guard let results = request.results as? [VNClassificationObservation],
          let firstResult = results.first else { return }
    // 解析した結果を取得
    self.number = firstResult.identifier
}

最後に作成したリクエストをVNImageRequestHandlerを使用して実行します。引数には解析対象の画像をCGImage型で渡します。

// VNImageRequestHandlerの作成とリクエストの実行
let handler = VNImageRequestHandler(cgImage: cgImage)
do {
    try handler.perform([request])
} catch {
    print("Failed to perform request: \(error.localizedDescription)")
}

Swift UIで画像のピッカーと解析機能を実装する全体サンプルコードを貼っておきます。実際には手書きで数字を書き込める機能などを実装した方が良いかもですね!

import SwiftUI
import CoreML
import Vision
import Combine
import PhotosUI

struct ContentView: View {
    
    @State  var show = false
    @State  var number = ""
    @State  var image: UIImage?
    
    var body: some View {
        VStack {
            
            Text("Number:\(number)")
            
            if let image = image {
                Image(uiImage: image)
                    .resizable()
                    .frame(width: 200, height: 200)
            }
            
            Button {
                show = true
            } label: {
                Text("PICKER")
            }
            
            Button {
                guard let cgImage = image?.cgImage else { return }
                // Core MLモデルの設定
                guard let model = try? VNCoreMLModel(for: MNISTClassifier(configuration: MLModelConfiguration()).model) else { return }
                
                // VNCoreMLRequestの作成
                let request = VNCoreMLRequest(model: model) { request, error in
                    if let error = error {
                        print("Error: \(error.localizedDescription)")
                        return
                    }
                    
                    guard let results = request.results as? [VNClassificationObservation],
                          let firstResult = results.first else { return }
                    // 解析した結果を取得
                    self.number = firstResult.identifier
                }
                
                // VNImageRequestHandlerの作成とリクエストの実行
                let handler = VNImageRequestHandler(cgImage: cgImage)
                do {
                    try handler.perform([request])
                } catch {
                    print("Failed to perform request: \(error.localizedDescription)")
                }
            } label: {
                Text("解析")
            }
        }.sheet(isPresented: $show, content: {
            ImagePickerDialog(image: $image)
        })
    }
}

#Preview {
    ContentView()
}

struct ImagePickerDialog: UIViewControllerRepresentable {

    @Binding  var image: UIImage?

    @Environment(\.presentationMode) var presentationMode

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePickerDialog>) -> PHPickerViewController {
        var configuration = PHPickerConfiguration()
        configuration.filter = .images
        configuration.selectionLimit = 1
        let picker = PHPickerViewController(configuration: configuration)
        picker.delegate = context.coordinator
        return picker
    }

    func updateUIViewController(_: PHPickerViewController, context _: UIViewControllerRepresentableContext<ImagePickerDialog>) {}

    class Coordinator: NSObject, UINavigationControllerDelegate, PHPickerViewControllerDelegate {
        var parent: ImagePickerDialog

        init(_ parent: ImagePickerDialog) {
            self.parent = parent
        }

        func picker(_: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
            parent.presentationMode.wrappedValue.dismiss()

            if let itemProvider = results.first?.itemProvider, itemProvider.canLoadObject(ofClass: UIImage.self) {
                itemProvider.loadObject(ofClass: UIImage.self) { [weak self] image, _ in
                    guard let self = self else { return }
                    guard let image = image as? UIImage else { return }
                    DispatchQueue.main.sync {
                        self.parent.image = image
                    }
                }
            }
        }
    }
}
【Swift/iOS】Core MLの実装方法!機械学習で画像解析

ちなみにこの「MNIST」が対応しているのは1桁の手書き数字のみなので2桁になると期待通りには取得できないので注意してください。

【Swift/iOS】Core MLの実装方法!機械学習で画像解析

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index