🫥

iOS16から使える画像認識について!

2023/12/09に公開

これは株式会社TimeTree Advent Calendar 2023の9日目の記事です。

https://qiita.com/advent-calendar/2023/timetree

はじめに

こんにちは、TimeTree iOS engineerの伊藤です。

私はいろいろ手をつけてみるのが好きで、良いものはすぐアプリに取り込みたいと考えてしまうタイプです。画像認識や生成AI技術についてもそうで、iOSでは素晴らしいフレームワークがありプロジェクトにもすぐ取り込むことができました。

この記事では「予定スキャン機能」について実装した時の画像認識技術について調べてまとめたものをご紹介していきます。


iOSの画像認識技術の概要

現在(2023/12)iOSでは、画像認識するためのフレームワークが2つ用意されています。

今回はフレームワークを使用して画像からテキストを抽出するOCR機能を活用します。

最近ではGPT-4-Turbo Vision APIが画像の入力に対応していますのでそちらでも試してみました。

  • Vision: 写真やビデオからの情報を処理・解析するためのフレームワーク。
    • TextRecognizerでOCRを利用できる。

https://developer.apple.com/documentation/vision/

  • VisionKit: 画像内の特定の要素を認識し分析するためのフレームワーク。
    • ImageAnalyzer/LiveTextでテキストやQRを簡単に読み込み+表示を行うことができる。

https://developer.apple.com/documentation/visionkit

  • gpt-4-vision-preview: こちらのモデルでは画像の入力も対応できるようになっています。
    • maxtokenが4096となっていますので、あまり大きい画像から大量に出力しようとすると途切れてしまいます。

https://openai.com/blog/new-models-and-developer-products-announced-at-devday


今回テストで利用する画像です。これがどのように出力されるか見ていきましょう!


Visionフレームワークの例

Vision/VNRecognizeTextRequestを使って、画像からテキストを抽出する基本的なコードは以下のようになります。
シンプルかつ取得結果が1度オブジェクト位置で取得できるので、カスタマイズも行いやすいです。

import Vision

@available(iOS 16.0, *)
final class TextRecognition {

    init() {}

    /// iOSの機能を利用して、画像からテキストを抽出を行う
    private func getTextRecognition(uiImage: UIImage) async -> String {
        guard let cgImage = uiImage.cgImage else { return "" }

        let requestHandler = VNImageRequestHandler(cgImage: cgImage)
        let request = VNRecognizeTextRequest()
        // 最高品質
        request.recognitionLevel = .accurate
        // 日本語指定
        request.recognitionLanguages = ["ja"]
        // 言語補正フラグ
        request.usesLanguageCorrection = true

        do {
            // OCRのリクエスト
            try requestHandler.perform([request])
            guard let observations = request.results else { return "" }
            var recognizedString = ""
            // OCRの結果のオブジェクトを整形する (指定pxに応じて改行やスペースを挿入しています)
            let observationLines = groupIntoLines(observations, yTolerance: 16 / uiImage.size.height, xTolerance: 4 / uiImage.size.width)
            // OCRの結果のオブジェクトテキストを生成する
            observationLines.forEach { observationLine in
                observationLine.forEach { observation in
                    if let topCandidate = observation.topCandidates(1).first {
                        recognizedString += topCandidate.string
                    } else {
                        recognizedString += " "
                    }
                }
                recognizedString += "\n"
            }
            return recognizedString
        } catch {
            return ""
        }
    }

結果

10:56: 80
【ANA国内線】 2023/12/26 ANA 983
予約のお知らせ受トレイ
ANA SKY WEB 122日
To 自分く
いつもANAをご利用いただきありがとうございます。
ご予約内容は以下の通りです。
7予約の確認、座席指定、購入手続き
http://rps.ana.co.jp/web/ncnv/uu/asm/all3216.php
■お支払い期限
航空券は2023123日(日)までにご購入ください。お支払い期限を過ぎると、ご予約は自動的に取り消しされます。
■ご搭乗者
イトウカズヤ様
■便情報
[120231226日(火)ANA 983
大阪(伊丹)(13:50-札幌(千歳)(15:35)
普通席
スーパーバリュー XXX
予約番号XXXX
[2202413日(水) ANA 778
札幌(千歳)(16:15-大阪(伊丹)(18:15)
普通席
スーパーバリュー XXX
予約番号XXXX
■運賃額等
43,320円
※変更・払い戻しの際は、航空券の有効期間にご注意くだ

VisionKitフレームワークの例

ImageAnalyzerでは画像を認識することができます。Macのプレビューでも利用することができ、日付などを直接クリックすることで予定を作成できたりします。

ただ今回欲しかったのは全体のテキストでしたので、それを抽出するコードのみをご紹介します🙏

ImageAnalyzer/LiveTextを使って、画像からテキストを抽出する基本的なコードは以下です。
シンプルですが出力結果をカスタマイズすることは難しい印象です。

import VisionKit

@available(iOS 16.0, *)
final class ImageAnalyzation {

    private let analyzer = ImageAnalyzer()

    // ImageAnalyzerを使用して画像からテキストを抽出する関数
    func analyzeImageForText(image: UIImage) async throws -> String {
        var configuration = ImageAnalyzer.Configuration(.text)
        configuration.locales = ["ja"]
        do {
            let analysis = try await self.analyzer.analyze(image, configuration: configuration)
            return analysis.transcript
        } catch {
            throw error
        }
    }
}

結果

10:56
80
日
【ANA国内線】 2023/12/26 ANA 983
予約のお知らせ
受トレイ
00O
ANA SKY WEB 122日
To 自分♥
いつもANAをご利用いただきありがとうございます。
ご予約内容は以下の通りです。
7予約の確認、座席指定、購入手続き
http://rps.ana.co.jp/web/ncnv/uu/asm/all3216.php
■お支払い期限
航空券は2023123日(日)までにご購入ください。お支払い期限を過ぎると、ご予約は自動的に取り消しされます。
■ご搭乗者
イトウ カズヤ様
■便情報
[120231226日(火) ANA 983
大阪(伊丹)(13:50-札幌(千歳)(15:35)
普通席
スーパーバリュー XXX
予約番号XXXX
[2202413日(水) ANA 778
札幌(千歳)(16:15-大阪(伊丹)(18:15)
普通席
スーパーバリュー XXX
予約番号XXXX
■運賃額等
43,320円
※変更・払い戻しの際は、航空券の有効期間にご注意

ChatCompletionAPI (gpt-4-vision-preview)での例

APIリクエストを行う例です!max_tokensを指定しないとエラーになってしまいますのでご注意ください。
ここでは最大値の4096をセットしています。少ない数値で出力しようとするとMAXに達した時点でレスポンスのコンテントが切られて出力されることがあります。

curl https://api.openai.com/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
  "model": "gpt-4-vision-preview",
  "n": 1,
  "presence_penalty": 0,
  "temperature": 0,
  "stream": 0,
  "frequency_penalty": 0,
  "top_p": 1,
  "max_tokens": 4096,
  "messages": [
    {
      "content": "OCRして日本語を出力してください!",
      "role": "system"
    },
    {
      "content": [
        {
          "text": "私の日程表",
          "type": "text"
        },
        {
          "image_url": {
            "url": "data:image/png;base64,$IMAGE_DATA_BASE64_STRING"
          },
          "type": "image_url"
        }
      ],
      "role": "user"
    }
  ]
}'

結果

ChatGPTのWebGUIからだと日本語が難しいと返答があったりしましたが、APIだと全然問題ないようです。今回の実験ではリクエスト完了までに10秒程度かかっています。

トークン数 (prompt_tokens : 1473 / completion_tokens : 520 / total_tokens : 1993)

10:56
[ANA国内線] 2023/12/26 ANA 983
予約のお知らせ
NA SKY WEB 122日
To 自分
いつもANAをご利用いただきありがとうございます。
ご予約内容は以下の通りです。

マネジの確認、座席指定、購入手続き
http://rps.ana.co.jp/web/ncnv/uu/asm/all3216.php

○ お支払い期限
航空券は20231231()までにご購入ください。お支
払い期限を過ぎると、ご予約は自動的に取り消しとなり
ます。

○ ご搭乗者
イワト カズヤ様

○ 便情報
[1] 20231226() ANA 983
大阪(伊丹)(13:50) - 東京(羽田)(15:35)
普通席
スーパーシートxxx
予約番号xxxx

[2] 202413() ANA 778
東京(羽田)(16:15) - 大阪(伊丹)(18:15)
普通席
スーパーシートxxx
予約番号xxxx

○ 運賃総額
43,320円

※変更・払い戻しの際は、航空券の有効期間にご注意ください

最新技術をプロダクトで活かす

今までであれば画像認識を行うのにも一手間も二手間も必要でした。

現代では生成AIを活用する手法も登場し、こういった技術へ専門的な知識がなくてもPoC作成ができるようになっています。

様々な活用方法を考えて便利に使える機能を提供していきたいと思っています!

画像認識も生成AIは日々進化しており、気づいたらフレームワークされて自由に利用できるようになっています。

正直追っていくだけで大変な時期かもしれませんが、エンジニアとしてこれらの技術を活用してプロジェクトを進化させられるのも醍醐味ですよね。
これからも、新しい技術の発展に目を向け、楽しみながら学んでいきたいと思います!


終わりに

TimeTreeでは自律プロジェクト制度という制度があり、今回のご紹介した予定スキャン機能もこちらの制度を活用して進められたものになります!入社わずかでも意思があればプロダクト開発に関わることができます!

こちらを読んでTimeTreeに少しでも興味を持っていただけたなら、ぜひ以下のページもチェックしてみてください。カジュアル面談などもお待ちしております!!

https://timetreeapp.com/intl/ja/corporate/careers

https://open.talentio.com/r/1/c/timetree/pages/29064

TimeTree Tech Blog

Discussion