This content originally appeared on Twilio Blog and was authored by Stephenie Minami Nakajima
背景
「OCR」という技術を耳にしたことがありますか?OCRは、Optical Character Recognition(光学文字認識)の略です。手書きや印刷された文字を光学的な手段でデータとして取り込み、文字認識することによってコンピュータープログラムなどで使用できるように変換する技術です。OCRは様々な分野で利用されています。使用例としては、車のナンバープレートを認識し盗難車を検知したり、書籍をデジタル化したりという例があります。Tesseract.jsは100以上の言語に対応するオープンソースのOCRライブラリです。Tesseract.jsは、C言語で開発されたTesseractOCRエンジンをJavaScriptのWebAssemblyにコンパイルしています。Tesseract.jsを使うと、ブラウザでOCRを簡単に利用できます。
Tesseract.jsの精度は完全ではありません。誤認識する可能性がありますので、これまで手作業で行っていた作業をTesseract.jsで自動化し効率を上げるためなど、補助的な用途での利用を推奨します。
本稿では、Tesseract.jsとReactを使用し、ブラウザ上で画像のOCR処理を行い、読み取ったテキストをSMSとして送信するアプリの作成方法をご紹介します。
本稿は前編と後編に分かれており、前編ではプロジェクトのセットアップからフロントエンドの構築までを、後編ではバックエンド側の構築と動作検証についてご紹介いたします。
後編はこちら:Tesseract.jsとReactでOCRコミュニケーションアプリを作る(後編)
目標
このチュートリアルを最後まで進めると、Tesseract.jsの基礎を学べるとともに、以下のようなReactを使ったOCRコミュニケーションアプリを作成できます。
アプリケーションの動作フローは以下を想定しています。
- 画像をアップロードする。
- Tesseract.jsで画像をOCR処理する。
- 必要に応じて読み取ったテキストを編集する。
- テキストを指定した電話番号にSMSとして送信する。
想定される技術知識
本稿では以下の知識を想定しています。
- JavaScriptの基本知識
- Node.jsの基本知識
- Reactの基本知識
必要なツール
- 安定バージョンのNode.jsとnpm
- Twilioアカウント。アカウント作成方法はHelp Centerの「Twilioアカウントの作成方法」を参照してください。
- Twilioの電話番号
アプリケーションの構造
作成するアプリケーションではフロントエンドとバックエンドを準備します。フロントエンド側では画像のアップロードボタン、OCR処理ボタン、テキストのエディタやSMSの送信ボタンなどを表示させます。バックエンド側はNode.jsとExpressを使ってSMSの送信処理を行います。
アプリケーションの具体的な構造は以下のとおりです。
フロントエンド:
-
App
: プロジェクトの実行エントリーポイントとなるルートコンポーネント。 OcrReader
: OCR処理する画像のアップロードとOCR処理ボタンを構成するコンポーネント。SmsSender
: OCR処理し読みとったテキストのエディタと、テキストのSMS送信ボタンを構成するコンポーネント。
バックエンド:
-
server.js
: Node.jsとExpressでSMS送信処理を行うサーバーファイル。
大まかなアプリケーションの構造が理解できたところでプロジェクトの作成に進みましょう。
基本設定とReactの準備
create-react-appでReactアプリケーションを作成する
まずは、Reactアプリケーションを作成します。
ターミナルを開いて、以下のコマンドを実行してください。
npx create-react-app ocr-sms-sender
cd ocr-sms-sender
npm start
このコマンドで、Reactアプリケーションの作成、ディレクトリへの移動、アプリケーションの起動を行います。
ブラウザでlocalhost:3000にアクセスします。問題なくアプリケーションが起動すると、以下のような画面が表示されます。
この時点で、一度ターミナルのプロセスを終了させてください。
依存パッケージをインストールする
次に、アプリケーションに必要な依存パッケージをインストールします。
ターミナルで以下のコマンドを実行してください。
npm install --save tesseract.js twilio express dotenv intl-tel-input
インストールした依存パッケージの詳細は以下のとおりです。
tesseract.js
: ブラウザで機能するJavaScriptOCRライブラリ。twilio
: Twilio Node ヘルパーライブラリ。Twilio APIに対するHTTPリクエストを、Node.jsを使って書けるようにするためのパッケージ。express
: Node.jsで使うウェブサーバーフレームワーク。本稿ではSMSの送信に使います。dotenv
: .envファイルに定義された値を環境変数として取り込むためのパッケージ。intl-tel-input
: International Telephone Input。国際電話の番号を入力して検証するためのJavaScriptプラグイン。
インストールが完了したら、次にフロントエンドを構築します。
フロントエンドを構築する
まずは、フロントエンドで構築するコンポーネントファイルを作成します。ターミナルで、/srcディレクトリの配下に、/componentsフォルダを作成してください。
/componentsフォルダの配下に、OcrReader.jsと、SmsSender.jsファイルを作成してください。
App.jsコンポーネントを構築する
ルートコンポーネントのApp.jsを構築します。create-react-app
を実行した際に自動作成された/src 配下にある App.jsファイルを編集します。テキストエディタでApp.jsファイルを開いてください。
ファイルの内容を、以下のコードに変更してください。
import { useState } from "react"
import OcrReader from "./components/OcrReader"
import SmsSender from "./components/SmsSender"
function App() {
const [ocrData, setOcrData] = useState("")
// 子コンポーネントからOCRデータをPropsとして受け取る
const onReadOcrData = (ocrData) => {
setOcrData(ocrData)
}
// 子コンポーネントで別の画像を使用するボタンが押されたことをPropsで検知する
const onRemoveClicked = () => {
setOcrData("")
}
return (
<div className="App">
<header>OCRアプリへようこそ!</header>
<OcrReader
onReadOcrData={onReadOcrData}
onRemoveClicked={onRemoveClicked}
/>
{ocrData && <SmsSender readText={ocrData}/>}
</div>
)
}
export default App
ファイルを保存してください。
このコードでは、画像のOCR処理を担うOcrReader
コンポーネントと、OCR処理で読み取ったテキストの編集とSMS送信を担うSmsSender
コンポーネントをインポートしています。
App.jsでは、子コンポーネントのOcrReader
で読み取ったテキストをocrData
としてpropsオブジェクトで兄弟コンポーネントのSmsSender
に渡します。
onReadOcrData
関数で、ocrData
を受け取ります。<SmsSender>
のJSXの属性で、ocrData
をreadText
としてpropsで渡します。
onRemoveClicked
関数で、OcrReader
コンポーネントで「別の画像を使用する」ボタンがクリックされた際に、<SmsSender>
に渡すテキストのデータも初期化します。
OCR処理コンポーネントを構築する
次に、OCR処理する画像の選択機能、画像の表示、OCR処理ボタンを担う関数コンポーネントのOcrReader
を構築します。OcrReader.js
を開いてください。
ファイルに以下のコードをペーストしてください。
import { useState } from "react"
import { createWorker } from "tesseract.js"
// 画像のOCR処理ステータス
const STATUSES = {
IDLE: "",
FAILED: "OCR処理に失敗しました。",
PENDING: "OCR処理中...",
SUCCEEDED: "OCR処理完了",
}
export default OcrReader
このコードでは、Tesseract.jsのcreateWorker
関数をインポートをしています。
STATUSES
で、Tesseract.jsの画像のOCR処理ステータスをオブジェクトとして定義します。export default OcrReader
で、コンポーネントを親コンポーネントのApp.jsにエキスポートします。
次に、コンポーネントのメインの関数、OcrReader
を定義します。STATUSES
のブロックと、export default OcrReader
の間に、以下のコードをペーストしてください。
function OcrReader({onReadOcrData, onRemoveClicked}) {
const [selectedImage, setSelectedImage] = useState(null)
const [ocrState, setOcrState] = useState(STATUSES.IDLE)
const worker = createWorker()
// 画像のOCR処理
const readImageText = async() => {
setOcrState(STATUSES.PENDING)
try {
await worker.load()
// OCRで読み取りたい言語を設定
await worker.loadLanguage("jpn")
await worker.initialize("jpn")
const { data: { text } } = await worker.recognize(selectedImage)
await worker.terminate()
// 日本語テキストはスペースが入ってしまう可能性があるので、スペースを削除
const strippedText = text.replace(/\s+/g, "")
onReadOcrData(strippedText)
setOcrState(STATUSES.SUCCEEDED)
} catch (err) {
setOcrState(STATUSES.FAILED)
}
}
}
上記のコードを詳しく解説します。
OcrReader
関数のパラメーターで、onReadOcrData
とonRemoveClicked
をpropsとして親コンポーネントに渡します。OCR処理する画像が選択されているかに関するステート(selectedImage
)と、OCR処理の実行状況に関するステート(ocrState
)を、useState
フックで定義します。 Tesseract.jsのworker
を変数として定義し、インスタンス化します。
Tesseract.jsでの画像のOCR処理をreadImageText
非同期関数で定義します。
関数が呼び出されてすぐに、OCR処理ステータスをPENDING
に設定します。このステータスはTesseract.jsの処理ステータスが変わるたびに更新します。
worker
インスタンスにはいくつかのメソッドが存在します。まずは、load
メソッドを呼び出します。
OCR処理で読み取りたい言語をloadLanguage
メソッドで指定します。本稿では日本語を表すjpn
を使用します。
OCR処理を初期化するための、initialize
メソッドを呼び出します。パラーメーターで読み取る言語(jpn
)を指定します。
OCR処理の準備ができたので、処理を実際に開始するためのrecognize
メソッドを呼び出します。パラメーターで読み取る画像を指定します。
最後に、OCR処理完了のタイミングでOCR処理の終了、クリーンアップを行うterminate
メソッドを呼び出します。
Tesseract.jsでは、日本語を読み取る言語として指定すると、文字と文字の間に半角スペースが入ってしまうことがあります。これを防ぐために、text.replace(/+/g, ““)
でスペースを取り除きます。
次に、readImageText
関数のブロックの下に、以下のコードをペーストしてください。
// 別の画像を使用するボタンを押した時の処理
const handleRemoveClicked = () => {
setSelectedImage(null)
onRemoveClicked()
setOcrState(STATUSES.IDLE)
}
このコードでは、「別の画像を使用する」ボタンがクリックされた際に、 setSelectedImage
で選択された画像のステート、selectedImage
をnull
に更新します。onRemoveClicked
で親コンポーネントにステートを渡します。
最後に、コンポーネントのJSXを追加します。handleRemoveClicked
関数のブロックの下に、以下のコードをペーストしてください。
return (
<div>
{selectedImage && (
<div>
<img src={URL.createObjectURL(selectedImage)} alt="scanned file" />
</div>
)}
<div>
{selectedImage?
<div className="button-container">
<button onClick={readImageText}>画像をOCR処理する</button>
<button
className="remove-button"
disabled={ocrState === STATUSES.PENDING}
onClick={handleRemoveClicked}
>
別の画像を使用する
</button>
</div>
:
<>
<p>画像ファイルをアップロードしてください。</p>
<input
type="file"
name="ocr-image"
onChange={(event) => {
setSelectedImage(event.target.files[0])
}}
/>
<p>対応フォーマット:bmp、jpg、png、pbm</p>
</>
}
</div>
<div className="status">
{ocrState}
</div>
<br />
</div>
)
ファイルを保存してください。
これで、OcrReader
コンポーネントが完成しました。OcrReader
コンポーネントの全コードは、Githubリポジトリを参照してください。
SMS送信コンポーネントを構築する
次に、SMSを送信する関数コンポーネントのSmsSender
を構築します。SmsSender.js
を開いてください。
ファイルに以下のコードをペーストしてください。
import { useEffect, useState, useRef } from "react"
import "intl-tel-input/build/css/intlTelInput.css"
import intlTelInput from "intl-tel-input"
// SMS送信ステータス
const STATUSES = {
IDLE: "",
FAILED: "メッセージ送信に失敗しました。",
PENDING: "メッセージ送信中...",
SUCCEEDED: "メッセージ送信完了",
}
export default SmsSender
このコードでは、intl-tel-input
をインポートしています。また、STATUSES
で、SMSの送信ステータスをオブジェクトとして定義します。
次に、コンポーネントのメインの関数、SmsSender
を定義します。STATUSES
のブロックと、export default SmsSender
の間に、以下のコードをペーストしてください。
function SmsSender ({readText}) {
const [smsText, setSmsText] = useState(readText)
const [iti, setIti] = useState(null)
const [smsSendingStatus, setSmsSendingStatus] = useState(STATUSES.IDLE)
const inputRef = useRef(null)
// International Telephone Inputを初期化
const init = () => intlTelInput(inputRef.current, {
initialCountry: "jp"
})
// レンダー後にInternational Telephone Inputを初期化
useEffect(() => {
setIti(init())
}, [])
// SMS送信リクエスト
const sendSMS = async () => {
setSmsSendingStatus(STATUSES.PENDING)
const country = iti.getSelectedCountryData()
const num = `+${country.dialCode}${iti.telInput.value}`
await fetch("/send-sms", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ to: num, text: smsText }),
}).then((response) => {
// Check successful request status
if (response.status === 200) {
setSmsSendingStatus(STATUSES.SUCCEEDED)
} else {
setSmsSendingStatus(STATUSES.FAILED)
}
}).catch(() => {
// Catch network errors
setSmsSendingStatus(STATUSES.FAILED)
})
}
}
SmsSender
関数のパラメーターで、OcrReader
コンポーネントから渡されたreadText
をpropsとして親コンポーネントを通して受け取ります。送信するSMSのテキストのステート(smsText
)、SMSを送信する電話番号のステート(iti
)、SMSの送信処理のステート(smsSendingStatus
)をuseState
フックで定義します。
inputRef
を定義し、useRef
でinput要素でユーザーが入力する電話番号にアクセスします。
init
関数で、intl-tel-input
を初期化し、電話番号の入力ができるよう設定します。
useEffect
フックで、レンダーの結果が画面に反映された後にintl-tel-input
を初期化するように設定します。
sendSMS
関数で、後ほど作成するsend-sms
エンドポイントに対してSMS送信リクエストを送信します。
fetch
でHTTP POSTリクエストを送信します。ボディにOCR処理で読み取ったテキストを指定します。エンドポイントからのレスポンスをもとにSTATUS
でSMS送信ステータスを更新します。
次に、ユーザーが「SMSメッセージを送信」ボタンの動作を定義するhandleSubmit
関数を定義します。sendSMS
関数のブロックの下に以下のコードをペーストしてください。
// 送信ボタンが押されたタイミングでSMS送信する
const handleSubmit = e => {
e.preventDefault()
e.stopPropagation()
sendSMS()
}
このコードでは、「SMSメッセージを送信」ボタンがクリックされたタイミングでsendSMS
関数を呼び出します。
HTMLページ内でクリックイベントが発生し、処理が終了すると、画面遷移が起こります。これをpreventDefault()
で防ぎます。
また、クリックイベントが発生するとイベントが親要素へと伝播していきます。stopPropagation()
でこれ以上の伝播しないようにイベントの伝播を停止します。
最後に、コンポーネントのJSXを追加します。handleSubmit
関数のブロックの下に、以下のコードをペーストしてください。
return (
<div>
<form onSubmit={(e) => handleSubmit(e)}>
<div>検知されたテキストを編集:</div>
<div>
<textarea
rows="15"
cols="45"
name="name"
defaultValue={readText}
onChange={e => setSmsText(e.target.value)}
/>
</div>
<input
ref={inputRef}
id="phone"
name="phone"
type="tel"
/>
<div>
<button disabled={smsSendingStatus == "Sending Message..."} type="submit">SMSメッセージを送信</button>
</div>
</form>
<div className="status">
{smsSendingStatus}
</div>
</div>
)
ファイルを保存してください。
これで、SmsSender
コンポーネントが完成しました。SmsSender
コンポーネントの全コードは、Githubリポジトリを参照してください。
CSSを追加する
次に、アプリケーションのCSSを定義します。
テキストエディタで/srcの配下にあるindex.css
を開いてください。ファイルの内容を以下のコードに変更してください。
html *
{
font-family: 'Noto Sans Japanese', sans-serif;
}
.App {
text-align: center;
}
header {
color: #2F7AE5;
font-size: 30px;
}
img {
width: 280px;
}
textarea {
border: 1px solid #ccc;
}
button {
color: #fff;
background: #2F7AE5;
padding: 12px;
border-radius: 5px;
border: none;
margin: 3px;
cursor: pointer;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-transition: all .3s;
transition: all .3s;
}
button:hover {
background-color: #1c4b8d;
}
input[type=text], input[type=tel] {
padding: 12px 20px;
margin: 8px 0;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
width: 300px;
}
input[type=text] {
height: 400px;
}
.button-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.remove-button {
background: #7E7E7E;
}
.remove-button:hover {
background: #414141;
}
.status {
color: #2F7AE5;
}
/* International Telephone InputのCSS */
.iti__flag {background-image: url("/node_modules/intl-tel-input/build/img/flags.png");}
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
.iti__flag {background-image: url("/node_modules/intl-tel-input/build/img/flags@2x.png");}
}
ファイルを保存してください。
これで、フロントエンド側の準備ができました!
次のステップ
前編ではプロジェクトのセットアップからフロントエンド側の構築に関してご紹介しました。後編ではバックエンド側の構築方法と、アプリケーションの実装についてご説明いたします。
後半はこちら:Tesseract.jsとReactでOCRコミュニケーションアプリを作る(後編)
Twilio Blogに投稿してみたい方や、フィードバック、登壇、勉強会のお誘いなどお気軽にsnakajima[at]twilio.comまでご連絡ください。開発中のプロジェクトに関してはGithub(smwilk)を覗いてみて下さい。
This content originally appeared on Twilio Blog and was authored by Stephenie Minami Nakajima
Stephenie Minami Nakajima | Sciencx (2021-10-29T08:27:45+00:00) Tesseract.jsとReactでOCRコミュニケーションアプリを作る(前編). Retrieved from https://www.scien.cx/2021/10/29/tesseract-js%e3%81%a8react%e3%81%a7ocr%e3%82%b3%e3%83%9f%e3%83%a5%e3%83%8b%e3%82%b1%e3%83%bc%e3%82%b7%e3%83%a7%e3%83%b3%e3%82%a2%e3%83%97%e3%83%aa%e3%82%92%e4%bd%9c%e3%82%8b%ef%bc%88%e5%89%8d%e7%b7%a8/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.