前回の記事では、Vision フレームワークを利用して画像の内容に応じて処理を切り替えるサンプルアプリを実装しました。画像内の文字量をもとに OCR と物体認識を分岐し、画像からテキストを取得できるところまでを確認しています。
前回の記事はこちら:画像内容に応じて処理を切り替えるiOSアプリを実装してみた

本記事ではその続きとして、画像から取得したテキストに対して自然言語解析を行い、言語判定やキーワード抽出、感情分析といった情報を取得する処理を追加します。ただし今回は、すべての画像に対して一律に解析を行うのではなく、文字が多く含まれている場合のみ自然言語解析を適用する構成としています。画像の性質に応じて処理を切り替えるという前回の設計を維持したまま、テキスト解析を組み込んでいきます。

開発環境は Xcode 26.2、Swift、iOS 16 以上です。

全体構成

今回は既存のサンプルアプリをベースに機能を拡張しています。そのため、新たに追加するのは自然言語解析に関する処理のみです。

追加したファイルは以下の通りです。

TextAnalyzer:解析処理全体の制御
LanguageDetector:言語判定
KeywordExtractor:キーワード抽出
SentimentAnalyzer:感情分析

既存の OCR 処理(TextRecognizer)および分岐制御(ImageAnalyzer)はそのまま利用しつつ、テキストが多いと判断された場合(前回実装した文字量判定ロジック)にのみTextAnalyzer を呼び出す構成としています。

言語判定と既存コードの変更

まず、Natural Languageフレームワークを利用してテキストの言語を判定します。NLLanguageRecognizerを使用することで、入力された文章の主要な言語を取得できます。

LanguageDetector.swift では以下のように実装しています。

import NaturalLanguage

enum LanguageDetector {

    static func detectLanguage(from text: String) -> NLLanguage? {

        let recognizer = NLLanguageRecognizer()
        recognizer.processString(text)

        return recognizer.dominantLanguage
    }
}

今回の変更で重要になるのは、既存の分岐処理との統合です。ImageAnalyzer では、画像内の文字量をもとに処理を切り替えていますが、文字が多いと判断された場合の処理を以下のように変更します。

            if isTextImage {

                TextRecognizer.recognizeText(in: image) { text in

                    let analyzedText = TextAnalyzer.analyze(text: text)

                    completion("""
【テキスト解析結果】

\(analyzedText)
""")
                }
            } else {

これにより、OCRの結果をそのまま表示するのではなく、後続の自然言語解析結果を含めた形で出力されるようになります。一方で、文字が少ない画像については従来通り物体認識を実行するため、不必要な自然言語解析を行わない構成となっています。

テキスト解析処理の統合

ここまでで個別の解析処理を用意しましたが、実際のアプリではこれらをまとめて実行し、結果を一つの出力として扱う必要があります。そのため、各処理を統合するための TextAnalyzer を実装しています。

TextAnalyzer.swift では、言語判定、トークン分割、キーワード抽出、感情分析を順に実行し、最終的な表示用テキストを生成します。

import Foundation

enum TextAnalyzer {

    static func analyze(text: String) -> String {

        let language = LanguageDetector.detectLanguage(from: text)?.rawValue ?? "unknown"
        let keywords = KeywordExtractor.extract(text: text)
        let sentiment = SentimentAnalyzer.analyze(text: text)

        return """
言語: \(language)

キーワード:
\(keywords.joined(separator: ", "))

感情スコア:
\(sentiment)

--- 原文 ---
\(text)
"""
    }
}

このように各処理を一箇所に集約することで、ViewController や ImageAnalyzer 側では解析の詳細を意識することなく、テキストを渡すだけで一連の自然言語解析を実行できるようになります。

キーワード抽出

トークン分割した結果から、主に形容詞および副詞を抽出し、文章内に含まれる感情表現やその強さを把握します。名詞を抽出することで話題の把握は可能ですが、本記事では後続の感情分析との関連性を考慮し、感情傾向に影響を与えやすい語を対象としています。

実装にはNLTaggerを使用し、品詞解析の結果から該当する語のみを抽出しています。

import NaturalLanguage

enum KeywordExtractor {

    static func extract(text: String) -> [String] {

        let tagger = NLTagger(tagSchemes: [.lexicalClass])
        tagger.string = text

        var keywords: [String] = []

        let options: NLTagger.Options = [.omitWhitespace, .omitPunctuation]

        tagger.enumerateTags(in: text.startIndex..<text.endIndex,
                             unit: .word,
                             scheme: .lexicalClass,
                             options: options) { tag, range in

            if tag == .adjective || tag == .adverb {
                keywords.append(String(text[range]))
            }

            return true
        }

        return keywords
    }
}

ただし、この.lexicalClass(品詞分類)は言語によって利用可能な範囲が異なります。日本語環境では利用可能な TagScheme が制限されており、.lexicalClassはサポート対象に含まれていません。

一方で、「サポートされていない場合に実際にどのような結果になるのか」については明確な情報が少ないため、本記事ではあえて言語ごとの分岐処理を行わず、日本語テキストに対しても同様の処理を適用する構成としています。その上で、実際の挙動を動作確認で検証します。

感情分析

文章全体の感情傾向を数値として取得します。sentimentScoreは -1.0 から +1.0 の範囲で返され、値が小さいほどネガティブ、大きいほどポジティブな傾向を示します。0 に近い場合は中立、または判断が難しい状態とみなされます。

import NaturalLanguage

enum SentimentAnalyzer {

    static func analyze(text: String) -> Double {

        let tagger = NLTagger(tagSchemes: [.sentimentScore])
        tagger.string = text

        if let score = tagger.tag(at: text.startIndex,
                                  unit: .document,
                                  scheme: .sentimentScore).0 {

            return Double(score.rawValue) ?? 0
        }

        return 0
    }
}

動作確認

画像の分岐処理(物体認識と OCR の切り替え)については前回の記事と同様のため、本記事ではテキスト解析部分に絞って確認します。

まず、日本語の文章を含む画像を読み込み、解析を行いました。

OCR によってテキスト自体は正しく抽出されましたが、「キーワード」と「感情スコア」については空、もしくは 0 となるケースが確認されました。事前情報として、日本語では.lexicalClassによる品詞分類がサポートされていないことは知られていますが、今回の実装のように分岐せず適用した場合には、キーワード抽出結果は空となる挙動になることが確認できました。

また、感情分析についても英語と比較すると安定した結果が得られない場合があります。

このように、日本語テキストに対する自然言語解析は、利用できる機能や結果の安定性に制約があることが、実際の動作を通じて確認できます。

次に、ネガティブな内容を含む英語の文章が記載された画像を読み込み、解析を行います。

この場合、OCR によりテキストが抽出された後、自然言語解析が実行され、感情スコアは -1.0の値となりました。これはネガティブな感情が強い文章であることを示しています。

続いて、ポジティブな内容の英語文章を含む画像についても同様に解析を行います。

この場合、感情スコアは 1.0となり、ポジティブな傾向が数値として表現されることを確認できました。

このように、本アプリでは画像から抽出されたテキストに対して自然言語解析を行うことで、文章の感情傾向を数値として取得できることが確認できます。特に英語テキストにおいては、感情の方向性が比較的分かりやすくスコアに反映される結果となりました。

おわりに

今回はNatural Languageフレームワークを利用して、OCR で取得したテキストに対する自然言語解析を追加しました。画像から文字を抽出するだけでなく、その内容に応じて解析処理を切り替えることで、より自然なアプリの振る舞いを実現できます。

一方で、言語による精度差や、解析結果が直感と一致しないケースも確認されました。これらの特性を踏まえた上で活用することが重要になります。もし興味があれば、ぜひ実際に手元で動かしながら試してみてください。



ギャップロを運営しているアップフロンティア株式会社では、一緒に働いてくれる仲間を随時、募集しています。 興味がある!一緒に働いてみたい!という方は下記よりご応募お待ちしております。
採用情報をみる