MoreBeerMorePower

Power Platform中心だけど、ノーコード/ローコード系を書いてます。

AI Builder の テキスト認識で読み取った結果を整形する - Power Automate での方法

前回は、AI Builderのテキスト認識を利用して Power Appsのアプリでレシート画像を読み込み、レシートの行の構造を再構成する方法を紹介しました。

AI Builder の テキスト認識で読み取った結果を整形する - Power Apps での方法 - MoreBeerMorePower

その際重要だったのは、画像から読み取った文字を、座標で左から右、上から下に並び替え、さらに折り返し判定を行うことでした。

https://cdn-ak.f.st-hatena.com/images/fotolife/m/mofumofu_dance/20220202/20220202000815.png

今回は Power Automateで同じことをやってみます。考え方自体は同じなので割愛して、具体的なフローの解説です。

f:id:mofumofu_dance:20220202230658p:plain

Power AppsとPower Automateで大きく異なるのは、ソート機能の有無です。 Power Appsでは SortByColumnsという関数で並び替えを容易に実現できましたが、Automateにはありません!

ということで、仕方ないのでデータソースを使います。 みんな大好き SharePointリスト。

Power Automateでの実装

フローは長いのでまず全体の概要です。フローは大きく3つのブロックから構成されます。

  1. トリガー+ファイルコンテンツ取得+画像解析
  2. 解析結果のリストへの登録とソートしての再取得
  3. 改行文字の挿入と文字列整形

f:id:mofumofu_dance:20220202232014p:plain

1. トリガー+ファイルコンテンツ取得+画像解析

トリガーは何でもよいのですが、今回はTeamsに投稿された画像ファイルをSharePointのドキュメントライブラリから取得してテキスト認識に追加しています。

f:id:mofumofu_dance:20220202233346p:plain

AI Builderでの解析結果はPower Appsの場合と同様です。

f:id:mofumofu_dance:20220202233524p:plain

このアクションから得られる動的コンテンツはちょっと癖があります。結果が入っているlinesという配列までが遠いんですよね。何もしないと2重ループになります。

もし画像を解析したのであれば、以下のような式を使ってループを極力減らすようにするとフローがすっきりします。

first(outputs('Recognize_text_in_an_image')?['body/responsev2/predictionOutput/results'])?['lines']

outputs('Recognize_text_in_an_image')はアクションの名前に依存します。テキスト認識のアクションの名前を揃えていただければコピペで使えます。

このあたり、ループを減らす方法については過去の投稿をご覧ください。

first関数で不要なApply to eachを回避する - MoreBeerMorePower

2.解析結果のリストへの登録とソートしての再取得

今回のキモです。

Power Automateは残念ながらソート機能がありません。そのため複雑な方法をとらず、カスタムコネクターやAzure Functionsを使わずにソートをしようと思うと、なんらかのデータソースに一回登録する必要があります。

1つの列でのソートであればExcelファイルでもよいのですが、今回はPower Appsの場合と同様にテキストの座標のTop, Leftでソートします。この場合もっとも手軽なのはSharePointリストかと思います。

本来はアイテムの削除処理をちゃんとやっておけばいいのですが、今回はguid()関数を使って どのランで登録されたアイテムなのかを区別できるようにしています。 (ソートしてアイテムを取得するときに生成したGUIDでフィルターしている)

ループを追加して、first(outputs('Recognize_text_in_an_image')?['body/responsev2/predictionOutput/results'])?['lines']を追加します。

リストへの登録では以下のように登録しています。

  • Title : item()?['Text']
  • guid (カスタム列) : 生成したGUID
  • left (カスタム列) : item()['boundingBox']?['left']
  • top (カスタム列) : item()['boundingBox']?['top']
  • right (カスタム列) : add(item()['boundingBox']?['left'],item()['boundingBox']?['width'])

f:id:mofumofu_dance:20220202235114p:plain

最後にリストからソート順 (OrderBy)を指定してアイテムを取得しています。top asc, left asc です!これが大事!

改行文字の挿入と文字列整形

あとは比較的簡単です。もう一回ループを入れて文字列結合をしていくだけです。ループなくせますがわかりやすさ重視で。

f:id:mofumofu_dance:20220203000141p:plain

ループの対象は range(0,sub(length(outputs('Get_items')?['body/value']),1)) 0から始まる(アイテム数-1) 個の数字配列です。

条件判定部分では、リストからの取得結果のN行目の右端とN+1番目の左端を比べています。

(outputs('Get_items')?['body/value']?[items('Apply_to_each')]?['right'] is greater than outputs('Get_items')?['body/value']?[add(items('Apply_to_each'),1)]?['left'])

f:id:mofumofu_dance:20220203000500p:plain

最後に取得したアイテムの最終行を追加して終わりです。

f:id:mofumofu_dance:20220203000740p:plain

おわり

Power Automateだとソート部分を実現するためにデータソースへの出し入れが発生するので、どうしても長くなってしまいます。 ただ読み取った結果をそのままTeamsで返してしまうとメッセージが縦長になりすぎたので、今回のようにある程度再構成して結果を返すのはかなり有効かなと思います。

もしフローを試してみたいという場合にはTwitterなどでお声がけください。