はじめに
業務で扱ったOCRを実装してみた。
題材は千と千尋の神隠しの湯婆婆が千尋の名前を取るシーン
以下記事がおもしろくてやってみたくなった
Javaで湯婆婆を実装してみる
「入力は手書き」 そして 「入力内容をCloud Vision APIで読み取らせて例の処理を実行」 することにした
コード
import React, { useRef, useState } from 'react';
import {
StatusBar,
SafeAreaView,
StyleSheet,
Text,
View,
TouchableOpacity,
} from 'react-native';
import RNDrawOnScreen from 'react-native-draw-on-screen';
import ViewShot, { captureRef } from 'react-native-view-shot';
// GCPで取得したAPIキー
const GCP_API_KEY = '';
const Yubaba = () => {
const RNDraw = useRef(null);
const RNViewShot = useRef(null);
const [name, setName] = useState('');
const { length } = name;
const newName = name.substr(Math.floor(Math.random() * length), 1);
// Cloud Vision API call
const callCloudVisionAPI = async (data) => {
const body = JSON.stringify({
requests: [
{
features: [
{
// テキスト認識
type: 'TEXT_DETECTION',
// 取得したい結果数
maxResults: 1,
},
],
image: {
// base64エンコードした画像をそのままセット
content: data,
},
},
],
});
const response = await fetch(
`https://vision.googleapis.com/v1/images:annotate?key=${GCP_API_KEY}`,
{
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
method: 'POST',
body: body,
}
);
const resJson = await response.json();
const description = resJson.responses[0]?.textAnnotations[0]?.description || '';
if (description) {
setName(description.replace(/\r?\n/g, ''));
}
};
// クリアボタン押下イベント
const onPressClear = () => {
RNDraw?.current?.clear();
setName('');
};
// OKボタン押下イベント
const onPressOk = () => {
captureRef(RNViewShot, { result: 'base64' }).then(callCloudVisionAPI);
};
return (
<>
<StatusBar barStyle="dark-content" />
<SafeAreaView style={styles.container}>
<View style={styles.contentWrap}>
<Text>{`契約書だよ。そこに名前を書きな。`}</Text>
</View>
<ViewShot style={styles.canves} ref={RNViewShot}>
<RNDrawOnScreen penColor={'black'} strokeWidth={10} ref={RNDraw} />
</ViewShot>
<View style={styles.buttonWrap}>
<TouchableOpacity
style={[styles.button, { borderColor: '#007AFF' }]}
onPress={onPressOk}
>
<Text style={[styles.buttonText, { color: '#007AFF' }]}>{`OK`}</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, { borderColor: '#FF7A00' }]}
onPress={onPressClear}
>
<Text style={[styles.buttonText, { color: '#FF7A00' }]}>{`クリア`}</Text>
</TouchableOpacity>
</View>
{!!name && (
<View style={styles.contentWrap}>
<Text>{`フン。${name}というのかい。贅沢な名だねぇ。`}</Text>
<Text>{`今からお前の名前は${newName}だ。いいかい、${newName}だよ。\n分かったら返事をするんだ、${newName}!!`}</Text>
</View>
)}
</SafeAreaView>
</>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
contentWrap: {
width: '100%',
margin: '4%',
marginTop: '8%',
},
canves: {
height: '30%',
margin: '2%',
borderWidth: 2,
borderColor: '#ccc',
backgroundColor: '#FFF',
},
buttonWrap: {
margin: '2%',
width: '100%',
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
},
button: {
borderWidth: 1,
margin: '2%',
padding: 10,
borderRadius: 4,
},
buttonText: {
fontSize: 14,
},
});
手書きUI
手書きのUIには、react-native-draw-on-screenを使用しています。文字認識を容易にするため、文字色は黒で線の太さも固定しています。さらに、手書きした内容を一時的に画像として保存したいため、react-native-view-shotを使用して画像化したい要素をラップしています。
import RNDrawOnScreen from 'react-native-draw-on-screen';
import ViewShot, {captureRef} from 'react-native-view-shot';
// ...略
<ViewShot style={styles.canves} ref={RNViewShot}>
<RNDrawOnScreen penColor={'black'} strokeWidth={10} ref={RNDraw} />
</ViewShot>
キャプチャ
手書きが完了したら、react-native-view-shotを使用してキャプチャを撮影します。CloudVisionAPIには、base64形式でデータを送信するためにオプションを指定しています。オプションを指定しなかった場合は、キャプチャが端末ストレージ内に保存され、そのパスが"data"に格納されます。
★プロジェクトで学んだことが生きた。。。。
// react-native-view-shotで描画部分のキャプチャを取得
captureRef(RNViewShot, {result: 'base64'}).then(async (data)
CloudVisionAPIへ送信
Cloud Vision(doc)
1000リクエスト/月までは無料。
// リクエストボディ作成
const body = JSON.stringify({
requests: [
{
features: [
{
// テキスト認識
type: 'TEXT_DETECTION',
// 取得したい結果数
maxResults: 1,
},
],
image: {
// base64エンコードした画像をそのままセット
content: data,
},
},
],
});
// CloudVisionAPI実行
const response = await fetch(
'https://vision.googleapis.com/v1/images:annotate?key=' + GCP_API_KEY,
{
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
method: 'POST',
body: body,
},
);
実行結果
まとめ。ざっとコードの要点
-
import文: 必要なReact Nativeコンポーネントやライブラリをインポートしています。
-
GCP_API_KEY: Google Cloud Vision APIにアクセスするためのAPIキーが定義されています。APIキーはセキュリティのため、実際の値が示されていないことに注意してください。
-
Yubabaコンポーネント: アプリケーションのメインコンポーネントが定義されています。
-
useRefフック: RNDrawとRNViewShotの2つのリファレンスが作成されています。これらは、描画部分を手書きするためのコンポーネント(RNDrawOnScreen)と、描画部分をキャプチャするためのコンポーネント(ViewShot)への参照を保持します。
-
useStateフック:
name
という状態変数が定義されています。手書きの名前が入力された後、ここに名前がセットされて表示されます。 -
onPressClear関数: クリアボタンが押されたときに、手書きの名前をクリアして
name
を空にします。 -
onPressOk関数: OKボタンが押されたときに、手書きの名前をテキストとして認識し、
name
にセットします。 -
captureRef関数: ViewShotを使用して描画部分をキャプチャします。キャプチャした画像は、Google Cloud Vision APIに送信されます。
-
fetch
を使用して、Cloud Vision APIに対してPOSTリクエストを送信し、手書きの名前をテキストとして認識します。 -
stylesオブジェクト: アプリケーションのスタイルが定義されています。
-
return文: アプリケーションのUIがJSXで記述されています。View、Text、TouchableOpacityなどのコンポーネントが組み合わさっています。
-
ViewShotの中にRNDrawOnScreenコンポーネントがあり、ユーザーが手書き入力を行えるようになっています。
-
「OK」ボタンと「クリア」ボタンがあり、それぞれ
onPressOk
とonPressClear
関数が紐づいています。 -
name
が空でなければ、手書きされた名前に対して湯婆婆のセリフが表示されます。