はじめに
先日Power AutomateとAI Builderを使って、レシート画像を読み取った方法のAzure版です。
この画像にあるように、ある程度の精度でレシートの行の構造を再現してみるという取り組みです。
ポイントになるのは、読み取り結果のテキストを、座標の左から右に、上から下になるように並び替えすることです。
Power Automateと同様に、Logic Appsにも配列のソート(並び替え)機能がないため、何らかのデータ置き場に一度読み取り結果を格納し、2つの軸で並び替えて取り出す必要があります。
今回は、そんな2つの軸で並び替えて(Order Byして) 取り出し可能なデータ置き場として、Cosmos DBを採用することにしました。
リソース作成~複合インデックス (2軸で並び替えするための設定) については備忘録として以下の投稿で書いていますので、参考にしてください。
全体の流れ
Logic Apps上のフローと各ブロックで大体何をしているのかを図示したものが以下になります。
フローのほうは小さくて見えないので、どのくらいの長さ・分量なのかの参考程度に。
また、今回のフローにはCosmos DBに登録したアイテムの削除処理が入っていません。通常は結果を返したあとにループやバッチで削除処理をいれますが、Cosmos DBでは自動削除機能 (Time to Live) がありますので、こちらを利用して、60分で消滅するようにします。
では各部詳細にみていきます。
1. 画像取得~Read API実行
このフローはLINE Botに画像を送って結果を送り返すことを念頭においていますが、今回は簡単のために適当なURLから画像取得をして、Read APIに渡してあげます。
https://raw.githubusercontent.com/mofumofu-dance/PowerApps365/master/media/ocr_source.jpg
Read API 特に画像ファイルを渡す場合の手続きは過去の投稿を参考にしてください。
このブロックのフローは以下の通りです。
2. Cosmos DBへのアイテム登録~ソートして取得
Read APIの結果は下図のような構造になっています。ほとんどAI Builderと同じですが、座標を表すboundingBoxの構成が少し違いますね。
ここはXY座標の組x4がずらずらっと並んでいます。読み方は以下を参照してください。
今回使うのはTopLeftのX座標、Y座標、TopRightのX座標です。それぞれboundingBox
の0番、1番、2番が対応しています。
読み取り結果の登録にはFor eachのループを使います。
first(body('HTTP_2')?['analyzeResult']?['readResults'])?['lines']
をFor eachの入力とします。
Cosmos DBへのアイテム登録には "Create or update document (V3)" のアクションを利用します。
"Document"のところには以下のJSONを設定してます。
{ "id": "@{guid()}", "runId": "@{outputs('Compose')}", "text": "@{items('For_each')?['text']}", "txtLeft": @{item()?['boundingBox']?[0]}, "txtRight": @{item()?['boundingBox']?[2]}, "txtTop": @{item()?['boundingBox']?[1]} }
このJSONを指定することで、読み取ったテキスト、その左上のXY座標、右上のX座標が保存されます。
登録した結果を取り出すには、”Query documents V5”のアクションを使います。
どの値をとってくるか、並び替えをどうするかを指定するための SQL Syntax Queryには以下を指定します。
select c.text, c.txtTop, c.txtLeft, c.txtRight from c order by c.txtTop asc, c.txtLeft asc
これで登録~並び替え結果の取得までできました。このブロックのフローは以下の通りです。
結果を見てみると、
なかなかいい感じで並び替え結果をとれましたね。(この時点ではあまりわからないけど)
3. 条件付きで文字列結合
さて、並び替えができたので、あとは文字列結合をしていくだけです。
配列の中で隣接しているアイテムのtxtRight
とtxtLeft
を比べて、折り返し判定を行っていきます。
ここからは完全にPower Automateの場合と同じです。
For eachループには range(0,sub(length(body('Query_documents_V5')?['value']),1))
を指定します。これは [0,1,2,..] という連続した長さが 元の配列の要素数-1 の整数配列を生成しています。
あとは body('Query_documents_V5')?['value']?[item()]?['text']
並び替えして取得した結果のN番目を取り出して、テキストを変数に追加していきます。
条件分岐は、結果のN番目の右端 (txtRight)とN+1番目の左端 (txtLeft)を比べています。
ループの外側では並び替えて取得したの最終行を追加しています。
これで条件付きで文字列結合ができました。
実際に実行してみる
サンプルの画像で読み取り~再構成の結果を見てみましょう。
結果は以下のようになりました。
@189x 3 *567 アソートドーナツ4個入り *298 白いわらびバニラ @118x 2 *236 *195 森永inゼリー マルチミネラル *323 明治 アーモンドガンダムコラボ 200 ドウブツノモリ キャラマグネッツ 53 レジ袋特大1枚 レジ袋大1枚 ¥2,767 計(税抜 8%) 小 ¥221 消費税等(8%) ¥208 計(税抜10%) 小 ¥20 消費税等(10%) ¥3,216 計 合 ¥2,988) (税率 8%対象 ¥228) (税率10%対象
いかがでしょう。ちょっとヨレている箇所で、ずれがありますが、まっすぐ写真取れているところはうまく行を再構成できていますね。
ということで、一応はノーコードの範囲でレシートの画像から文字を読み取り、それっぽく構造を再現することができました。
今回のフローはちょっと入り組んでいますので、以下からダウンロードいただけます。Logic Appsでコード表示してコピペするためのJSONです。
使い方はReadmeを読んでみてください。