⌨️

スマホで撮影した写真をOCRで文字起こしし、音声データに変換して再生する方法

2023/06/13に公開

はじめに

SREホールディングス株式会社にて、ソフトウェアエンジニアをやっております、叶です。
入社から色々なAI技術に触れる機会がありました。
今回は、開発で携わったAI-OCR技術を用いた簡単なアプリを作ってみました。スマホのカメラで写真を撮って、写真に含まれるもテキストを文字起こして、音声データに変換して読み上げるというアプリです。

使用した技術

  • フロント(React)
    • react:Javascriptのエコシステムフレームワーク
    • react-webcam: デバイスのカメラを操作し、撮影した画像データを取得するライブラリ
  • バックエンド(python)
    • flask: WEBアプリケーションライブラリ
    • EasyOCR:画像から文字を抽出するライブラリ
    • gTTs :文字を音声データに変換するライブラリ

フロント(React)

  1. 画面表示とAPI処理のソースコード
App.tsxのソースコード
App.tsx
import { useState } from "react";
import axios from "axios";
import Camera from "./Camera";
import AudioPlayer from "./AudioPlayer";

const dataURLtoBlob = (dataurl: string) => {
  let arr = dataurl.split(",");
  let mime = "image";
  let test = arr[0].match(/:(.*?);/);
  if (test !== null) {
    mime = test[1];
  }
  let bstr = atob(arr[1]),
    n = bstr.length,
    u8arr = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  return new Blob([u8arr], { type: mime });
};

const App = () => {
  const [isCaptureEnable, setCaptureEnable] = useState<boolean>(false);
  const [audioFile, setAudioFile] = useState<File | null>(null);
  const capture = (imageSrc: string) => {
    if (imageSrc) {
      let params = new FormData();
      params.append("image", dataURLtoBlob(imageSrc), "testImage.png");
      let post_api = axios.create({
        headers: {
          "Content-Type": "multipart/form-data",
          Accept: "multipart/form-data",
        },
      });
      post_api
        .post<File>("https://localhost:5000/api/img/ul", params, {
          responseType: "blob",
        })
        .then(
          (response) => response.status === 200 && setAudioFile(response.data)
        )
        .catch((error) => console.log(error));
    }
  };

  return (
    <>
      <header>
        <h1>カメラアプリ</h1>
      </header>
      {isCaptureEnable || (
        <button onClick={() => setCaptureEnable(true)}>開始</button>
      )}
      {isCaptureEnable && <Camera sendCapture={capture} />}
      {audioFile && <AudioPlayer mp3File={audioFile} />}
    </>
  );
};

export default App;
  1. ブラウザでスマホのカメラにアクセスし、写真を取得する
カメラのコンポーネントのソースコード
Camera.tsx
import { useRef, useState, useCallback } from "react";
import Webcam from "react-webcam";

const videoConstraints = {
  width: 720,
  height: 360,
  facingMode: "environment",
};

const Camera = (props: { sendCapture: (imageSrc: string) => void }) => {
  const webcamRef = useRef<Webcam>(null);
  const [url, setUrl] = useState<string | null>(null);
  const capture = useCallback(() => {
    const imageSrc = webcamRef.current?.getScreenshot();
    if (imageSrc) {
      props.sendCapture(imageSrc);
      setUrl(imageSrc);
    }
  }, [webcamRef]);

  return (
    <>
      <div>
	{/* Webカメラの関連設定 */}
        <Webcam audio={false} width={540} height={360} ref={webcamRef} screenshotFormat="image/jpeg" videoConstraints={videoConstraints} />
      </div>
      {/* カメラシャットダウン */}
      <button onClick={capture}>キャプチャ</button>

      {url && (
        <>
          <div>
            <button onClick={() => setUrl(null)}>削除</button>
          </div>
	  {/* 撮った写真のプレビュー */}
          <div><img src={url} alt="Screenshot" /></div>
        </>
      )}
    </>
  );
};

export default Camera;
  1. 受け取った音声データを再生する
オーディオプレーコンポーネントのソースコード
AudioPlayer.tsx
import React, { useState } from "react";

const AudioPlayer = (props: { mp3File: File }) => {
  const mytrack: HTMLAudioElement = document.getElementById(
    "mytrack"
  ) as HTMLAudioElement;
  const [audioSrc, setAudioSrc] = useState(
    URL.createObjectURL(new Blob([props.mp3File], { type: "audio/mp3" }))
  );
  return (
    <div>
      <audio id="mytrack" controls>
        <source src={audioSrc} />
      </audio>
    </div>
  );
};

export default AudioPlayer;

バックエンド(Python)

  1. 画像ファイルを受け取る
  2. EasyOCRで文字起こしを行う
  3. gTTsで音声データに変換する
  4. MP3ファイルを送信する
バックエンドのソースコード
app.py
@app.route("/api/img/ul", methods=["POST"])
def file_upload():
    # 受け取ったファイルを一旦保存
    file = request.files['image']
    print(file.filename)
    file.save("test.png")
        # EasyOCRで文字起こし
    reader = easyocr.Reader(['ja','en']) # this needs to run only once to load the model into memory
    results = reader.readtext('test.png', detail = 0)
    print(results)
    message = "".join(results)
        # 文字を音声データに変換
    tts1 = gTTS(text=message, lang='ja')
    tts1.save("test.mp3")
        # ファイルをレスポンスに設定
    return send_file("../../test.mp3")

動作確認

サンプル写真


出典:SREホールディングスの代表者メッセージ

生成した音声データ

https://drive.google.com/file/d/1Lv7fstS6tOf4kyl3SlTIGW8drKdHQhmF/view?usp=drive_link

リポジトリ

https://github.com/ZHIBINYE/ai-ocr-study

まとめ

サンプル写真くらい内容が簡単だと、追加の画像処理なしで文字起こしができるようです。
今回は新しく学んだ内容を活用して、自分が欲しいアプリを作れたのでなかなか面白かったです。
AIが進んでいる世の中、将来に「AI & 人」でAIが人の生活に馴染んでいきそうです。

SRE Holdings 株式会社

Discussion