画像の文章を要約するアプリ[Flutter,OCR,Chat-GPT]

yutanpoyutanpo1227

Top

Chat-GPT APIが話題だったので、使ってみたいと思い今回のアプリを作成しました。
概要はOCRで画像から文章を取得し、Chat-GPT APIで要約した文章を表示するアプリです。
※今回初めてFlutterを使用し、制作期間も3日程度だったのでかなり粗いつくりかもしれませんご了承ください。

目次

構成

構成

  • アプリ開発にはFlutterを使用、画像の選択、OCR、Chat-GPT APIの呼び出しを行っています。
  • OCRにはGoogle FirebaseのML Kitを使用しています。
  • 検出された文章はChat-GPT APIに渡し、要約した文章を取得し結果を表示します。

環境

  • 実行環境
    • iPhone13Pro max : iOS 16.0.2
  • 開発環境
    • macOS Monterey 12.6
    • Xcode 14.0.1
    • Flutter 3.7.7

Flutterでのアプリの作成

今回アプリを作成するにあたり、Flutterを使用しました。Swiftでもよかったのですが今回はFlutterに触れてみたかったと言うこともありFlutterを選択しました。

動作の流れとしては

  1. 画像の撮影、選択 + トリミング
  2. 画像をGoogle FirebaseのML Kitに渡してOCRで文章を取得
  3. 取得した文章をChat-GPT APIに渡して要約した文章を取得
  4. 取得した文章を画面に表示

こんな動作イメージで制作しました。

画面の作成

画面

画面は上記のようなに画面構成で、画面1では画像の選択と言語の選択を行い画面2に読み取った文章と要約結果を表示するようにしています。

個人的にごちゃごちゃしたデザインよりシンプルなデザインが好きなのでこのような最低限のデザインにしました。あとAppBarに色がついているのも嫌だったので透明にしました。

  @override
  Widget build(BuildContext context) {
    SystemChrome.setSystemUIOverlayStyle(
      const SystemUiOverlayStyle(
        statusBarBrightness: Brightness.light, 
      )
    );
    return Scaffold(
      appBar: AppBar(
        automaticallyImplyLeading:true,
        iconTheme: IconThemeData(color: Colors.black),
        elevation: 0,
        backgroundColor: Colors.transparent,
      ),

AppBarの色を透明にするには上記のようにAppBarのbackgroundColorを透明にしelevationを0し影を消すことで完全に透明にすることができます。
これだけだとステータスバーの色が白で背景に消えてしまうので、SystemChrome.setSystemUIOverlayStyleを使用してstatusBarBrightnessをBrightness.lightにして色を変更しています。

画像の選択、トリミング

今回は画像の選択にはimage_pickerを使用しトリミングにはimage_cropperを使用しました。

import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart';
class Pick{
  XFile? image;
  final picker = ImagePicker();

  // 画像をギャラリーから選ぶ関数
  Future<XFile?> pickImage() async {
    final image = await ImagePicker().pickImage(source: ImageSource.gallery);
    // 画像がnullの場合戻る
    if (image == null) return null;

    final imageTemp = image;

    return imageTemp;
  }
  // カメラを使う関数
  Future<XFile?> pickImageCamera() async {
    final image = await ImagePicker().pickImage(source: ImageSource.camera);
    // 画像がnullの場合戻る
    if (image == null) return null;

    final imageTemp = image;

    return imageTemp;
  }
}

image_pickerにはもともとカメラとライブラリから画像を選択する関数が用意されているのでそれを使用します。

Future cropImage(XFile img) async {
  final croppedFile = await ImageCropper().cropImage(
    sourcePath: img.path,
    uiSettings: [
    AndroidUiSettings(
        toolbarTitle: 'Cropper',
        toolbarColor: Colors.deepOrange,
        toolbarWidgetColor: Colors.white,
        initAspectRatio: CropAspectRatioPreset.original,
        lockAspectRatio: false),
    IOSUiSettings(
      title: 'Cropper',
    ),
    WebUiSettings(
      context: context,
    ),
    ]
  );
  if (croppedFile != null) {
    this.image =  XFile(croppedFile.path);
  }
}

必要な文字以外は切り抜けるようにトリミングもできるようimage_cropperを使用しました。

OCR

ML Kitを使用にはFirebaseのプロジェクトを作成し、ML Kitを有効にする必要があります。その際こちらを参考にさせていただきました。

FlutterでML Kitを使用するにはgoogle_mlkit_text_recognitionというライブラリを使用します。

import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';

画面1で選択した言語と画像をML Kitに渡し、OCRで文章を取得します。

Future ocr_ja () async {
  final InputImage imageFile = InputImage.fromFilePath(image!.path);
  final textRecognizer =
    TextRecognizer(script: TextRecognitionScript.japanese);
  final RecognizedText recognizedText =
      await textRecognizer.processImage(imageFile);
  String text = '';
  for(TextBlock block in recognizedText.blocks)
  {
    String temp = block.text;
    text = text + temp;
  }
  setState(() {
    result = Text(text.replaceAll('\n', ""));
  });
  textRecognizer.close();
}

Future ocr_en () async {
  final InputImage imageFile = InputImage.fromFilePath(image!.path);
  final textRecognizer =
    TextRecognizer(script: TextRecognitionScript.latin);
  final RecognizedText recognizedText =
      await textRecognizer.processImage(imageFile);
  String text = '';
  for(TextBlock block in recognizedText.blocks)
  {
    String temp = block.text;
    text = text + temp;
  }
  setState(() {
    result = Text(text.replaceAll('\n', ""));
  });
  textRecognizer.close();
}

今回はTextRecognizerの引数にjapaneseとlatinを選択することで日本語と英語バージョンを作成ししました。

  1. TextRecognizerに画像を渡すには画像をInputImageに変換すしなければいけないのでInputImage.fromFilePath(image!.path)で変換
  2. TextRecognizerにjapaneseかlatinを渡す
  3. TextRecognizerにInputImageを渡しブロックごとの文章を取得し文字列を結合
  4. 最後に読み取った文章そのままでは改行が入っているのでreplaceAllで改行を消す

Chat-GPTに要約してもらう

最後に読み取った文章をChat-GPTに渡し、文章を生成します。
ChatGPTをFlutterを使用するには以下のライブラリを使用します。

import 'package:dart_openai/openai.dart';

ChatGPT APIをFlutterで使用するにはまずAPIKEYを取得する必要があるのでこちらから取得してください。

Future main() async{
  await dotenv.load(fileName: '.env');
  OpenAI.apiKey = dotenv.get('API_KEY');
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
}

取得したAPIKEYをOpenAI.apiKeyに代入します。今回はソースコードを公開する予定だったので.envファイルを利用しています。

Future listenChatGPT(String message) async{
  answerText = '処理中';
  final chatCompletion = await OpenAI.instance.chat.create(
    model: "gpt-3.5-turbo",
    messages: [
      OpenAIChatCompletionChoiceMessageModel(
            content: "あなたはこれから入力される文章を日本語で要約してください",
            role: "system",
        ),
      OpenAIChatCompletionChoiceMessageModel(
            content: message,
            role: "user",
        ),
    ],
  );
  setState(() {
    answerText = chatCompletion.choices.first.message.toMap()['content'].toString();
  });
}

先ほど読み取った文章を引数で渡し、ChatGPTに渡します。
ChatGPTに渡す際roleを指定することができ

  • system: AIの設定を記述
  • assistant: AIからの発言
  • user: ユーザーからの発言

となっています。今回は「あなたはこれから入力される文章を日本語で要約してください」とsystemに記述することでAIに文章を要約してもらうように指示しています。
userの文章としては先ほど読み取った文章を渡しています。
ChatGPTから返ってきた文章をマップに変換しcontentのkeyを指定してあげることで返答だけを得ることができます。

実際に動作させてみる

動作
割といい感じに要約してくれるなって感じですね、今のChat-GPTだと時間がかかってしまいますが、Chat-GPT4を使えばもっと早くなるかも(?)って期待してます。
流石にFlutterを初めて3日で作ったものなので今後しっかり勉強してもっといいものにできたらいいなと思います。

ちなみにお金がないので、AppStoreに出せませんでした泣
代わりにGitHubにでもソースコード置いておきます
yutanpo1227/flutter_ocrApp