はじめに
ちょこっと画像からテキストを抽出したい時ありませんか?
この記事ではUiPath Extended Languages OCRアクティビティを用いて、画像からテキストを抽出してWindowsのクリップボードにコピーするワークフローを実装してみます。
トリガー ベースの有人オートメーションを使い、何度も繰り返し処理できるようにします。Orchestratorのトリガー機能と異なり、ロボット端末で発生するローカルなイベントをトリガーにしてワークフローを実行します。ローカルトリガーには様々な種類がありますが、この記事ではフォームアクティビティを使います。
文字抽出する他のオプション
Windows 11では、デフォルトで利用できるSnipping Toolを用いてキャプチャからテキストを抽出できます。
Snipping Tool を使ってスクリーン ショットをキャプチャする
UiPath Clipboard AIでは、複数の形式のドキュメント間でデータのコピーと貼り付けを行うことができます。※2024年8月時点でプレビュー版です。
インタラクティブに操作する他のオプション
UiPath Automation Cloudを利用している場合は、フォームよりも多機能なUiPath Appsを利用できます。
アプリと有人オートメーション: 双方向の即時通信によりワークフローと連携することも、Integration Serviceのコネクションや、Data Serviceのエンティティと連携することも簡単です。Autopilotという生成AIを利用して、従来より簡単にデザイン開発することもできます。
UiPath Action Centerで利用する他のオプション
長期実行ワークフローをAction Centerで利用するとき、フォームの他にAppsも利用できるようになりました。Action Centerで利用するAppsは、アクションアプリとして開発します。
OCRエンジンの種類
UiPath公式アクティビティでは、複数のOCRエンジンが用意されています。
UiPath.UIAutomation.Activities
パッケージをインストールした時に利用できるOCRエンジンは以下の通りです。
- Google Cloud Vision OCR
- Microsoft Azure Computer Vision OCR
- Tesseract OCR
- UiPath 画面 OCR
- UiPath ドキュメント OCR
- OCR - 日本語、中国語、韓国語(2025年1月より非推奨化の予定)
- UiPath Extended Languages OCR
UiPath Extended Languages OCRは日本語、中国語、韓国語を含む複数言語に対応します。
他のアクティビティパッケージをインストールしたり、『HTTP 要求』アクティビティを利用したりすることで、上記リストに記載のないOCRも利用できます。
OCRの使いどころ
UiPath製品では、以下のような場面でOCRを活用できます。
-
画像からテキストを取得します。本記事で実装するシンプルな使い方です
-
ワークフロー内で、UI要素を特定する際にセレクターの補助にすることもできます。セレクターをうまく設定できない場合に活用できます
-
UiPath Document Understandingと組み合わせることで、OCRをより活用できます
本記事では、Document Understandingについては取り扱いません
ワークフローを実装してみる
この記事では、次の2点を前提としております。
- UiPath Studio(Desktop版)を利用できること
- UiPath Automation Cloudに、AI ユニット(管理 > ライセンス > 消費状況)またはDocument Understanding(管理 > ライセンス > ロボットとサービス)のAPIキーがあること
UiPath Extended Languages OCRアクティビティの挙動確認
ワークフロー開発の前に、個々のアクティビティの挙動を確認しておきます。
UiPath Extended Languages OCRを利用するにはUiPath.OCR.Activities v3.20.2 以降が必要です。
この記事では、以下バージョンのアクティビティパッケージを用いました。
- UiPath.Form.Activities v24.10.2
- UiPath.System.Activities v24.10.4
- UiPath.UIAutomation.Activities v24.10.1 (← 依存関係としてUiPath.OCR.Activities v3.20.3を含む)
アクティビティパネルで「OCR」と検索し、『UiPath Extended Languages OCR』アクティビティを右クリックし、「テストベンチを作成」をクリックします。
AI ユニット(管理 > ライセンス > 消費状況)またはDocument Understanding(管理 > ライセンス > ロボットとサービス)のAPIキーをコピーし、『UiPath Extended Languages OCR』の「APIキー」プロパティにダブルクォテーションで囲って貼り付けます。また入力変数と出力変数を作成しておきます。
Automation Cloudの管理画面にアクセスできずAPIキーが分からない場合には、アクティビティパネルからデザイナーパネルへ、『UiPath Extended Languages OCR』アクティビティをドラッグアンドドロップしてみてください。利用できるAPIキーがある場合は自動で入力されます。
『UiPath Extended Languages OCR』アクティビティの入力変数の型は「Image」型となっているので、「Image」型の変数を用意する必要があります。
「Image」型の変数を出力するアクティビティの1つに、『スクリーンショットを作成』アクティビティがあります。
『スクリーンショットを作成』アクティビティではいくつかの方法でターゲットを指定できますが、ここでは以下のように設定します。
-
『画面上で指定』アクティビティで出力する「UiElement」型変数を、『スクリーンショットを作成』アクティビティの「入力要素」プロパティに指定する
-
『スクリーンショットを作成』アクティビティの出力先に「画像」を選択する。「保存された画像」プロパティには、『UiPath Extended Languages OCR』アクティビティの「画像」プロパティに作成しておいたImage型変数を指定する
この状態でデバッグ実行してみます。
- 『画面上を指定』アクティビティ実行時に、マウスをドラッグして画像領域を指定する
- ブレークポイントが設定された『UiPath Extended Languages OCR』アクティビティの実行直前で一時中断するので、Studioデバッグタブの「ステップ イン」をクリックして、1つ先のアクティビティに進む
- Studioローカルパネルにて、『UiPath Extended Languages OCR』アクティビティで指定した出力変数に格納された値を確認できます。意図した通りに文字が抽出できているか確認する
色々アクティビティの挙動を検証してみた結果、以下のことが分かりました。
- OCRで抽出した結果、日本語は文字と文字の間に半角スペースが入っている(2024年8月時点)
- 『画面上で指定』アクティビティは、「画面領域を選択」プロパティのチェックボックスにチェックする(=Trueに設定する)と、マウスをクリックしたままドラッグして領域を選択するモードのみ利用できる
アクティビティの挙動を試し終えたら、テストベンチは「×」ボタンで閉じて削除します。
フォームを作る
それでは実際にワークフローを作っていきます。
まずはユーザーと対話するためのフォームを作成します。
Studio左上の「新規」ボタンから新しいフォームを作成します。
作成したフォーム画面では、Studio左側にコンポーネントパネルが表示されます。コンポーネントパネルから「ラベル/ヘッダー」をドラッグして、真ん中のデザインパネルにドロップします。
ラベルサイズを「ヘッダー - 中」にし、ヘッダーとして表示したい文字列をコンテンツに入力して保存します。
次に、最初からある「確定」ボタンにマウスカーソルを当てると、右側にいくつかアイコンが表示されます。歯車アイコンの編集ボタンをクリックします。
「確定」ボタンを削除していた場合には、コンポーネントパネルから新しく「ボタン」をドラッグアンドドロップして編集します。
表示タブのラベルを「スタート」に変更し、アクションを「イベント」に変更して保存します。
コンポーネントパネルから「テキスト フィールド」をドラッグアンドドロップし、表示タブのラベルを「お知らせ」に変更し、
フィールドキータブのプロパティ名を「textField_info」に変更して保存します。
フォームを作成し終えたら、忘れずに保存しておきます。
※ファイル名右上に「*」がついているものは、未保存の変更があるファイルです。
ローカルトリガーの実装
次はローカルトリガーを実装します。
ローカルトリガーは、ロボット実行マシン上で発生するイベントをトリガーとして、任意のワークフローを実行します。
本記事ではフォームの「スタート」ボタンクリックをトリガーとして、OCRでテキスト抽出してクリップボードにコピーし、フォームが閉じたらローカルトリガーも停止するようにします。
- Main.xamlに『フォームを表示』アクティビティを配置し、先ほど作成したフォームを選択する。このアクティビティ実行後もフォームを表示し続けたままにしたいので、「ワークフローの実行を継続」トグルは「オン」にする
- Main.xamlの『フォームを表示』アクティビティの下に、『ローカル トリガーを実行』アクティビティを配置する
- 新しいワークフロー(シーケンス)を作成し、『フォーム イベント トリガー』アクティビティを配置する。先ほど作成したフォームを指定し、イベントに「フォームが閉じられた」を選択する。これで、このワークフローはフォームが閉じられた時実行されるようになる
- 『ローカル トリガーを停止』アクティビティを続けて配置する。フォームが閉じられたらローカルトリガーも停止するようになる。ローカルトリガーが停止するとMain.xamlの『ローカルトリガーを実行』アクティビティを抜けて、オートメーションプロジェクト全体が実行完了に向かう
ここまでできたら、プロジェクトを一度実行してみます。フォームが表示され、フォーム右上の「×」ボタンでフォームを閉じると、プロジェクトも実行完了することを確認できると思います。
『フォームを表示』アクティビティの出力引数は、「ワークフローの実行を続行」プロパティがOffの場合のみ取得できます。
ローカルトリガー機能を利用する場合には「ワークフローの実行を続行」プロパティをOnにする必要があります。
OCR処理するローカルトリガーの追加
次に、「スタート」ボタンをクリックした時の処理を追加します。
作成したフォームを表示し、「スタート」ボタンにマウスカーソルを合わせた時に表示される稲妻アイコンの「Create Trigger」をクリックします。
ワークフローの作成ポップアップが表示されるので、任意のワークフロー名を命名します。
新しく作成されたワークフローの先頭に『フォームイベントトリガー』アクティビティが配置され、イベントに「<ボタン名>のクリック」が設定されていることを確認します。
このワークフローは、「スタート」ボタンがクリックされたときに実行されます。
スケジュールモードを「SequentialCollapse」に変更しておきます。
スケジュールモードについて
SequentialCollapse - 現在のイベントの実行が完了するまで、今後発生するすべてのイベント (最新のイベントを除く) を無視します。
テストベンチで動作確認したように『UiPath Extended Languages OCR』アクティビティを設定します。
抽出したテキストは色々な処理方法が考えられますが、この記事ではクリップボードにコピーします。クリップボードにコピーしたデータは、「Ctrl + v」キーを押下すると貼り付けられます。
- 『画面上で指定』アクティビティを配置する。プロパティの「画面領域を選択」にチェックを入れ(=Trueに設定し)、「選択されたUI要素」の出力変数を作成する
- 『スクリーンショットを作成』アクティビティを配置し、出力先に「画像」を選択する。プロパティの「入力要素」に『画面上で指定』アクティビティの出力変数を指定し、「保存された画像」の出力変数を作成する
- 『UiPath Extended Languages OCR』アクティビティを配置する。プロパティの「APIキー」にAI ユニットまたはDocument UnderstandingのAPIキーを設定する。入力する画像として、『スクリーンショットを作成』アクティビティの出力変数を指定し、「テキスト」の出力変数を作成する
- 『クリップボードに設定』アクティビティを配置し、出力したテキスト変数を設定する
- 『フォームの値を設定』アクティビティを配置し、作成したフォームを指定する。引数の詳細エディターを開き、「textField_info」キーの値として
"読込み&コピー完了"
と設定する
プロジェクトを実行してみましょう。
「スタート」ボタンをクリックして画像領域を選択し、フォームに完了のお知らせが表示されるまで待ってからメモ帳などを開いて「Ctrl + v」キーを押下すると、画像内のテキストを貼り付けられたでしょうか?
機能追加
このワークフローはシンプルですが、いくつか改良できる点があります。
AI ユニット/Document UnderstandingのAPIキーを、ワークフローに埋め込みたくない
APIキーをワークフローに埋め込むことには、以下のデメリットがあります。
- APIキーが漏洩した際に、管理者の意図しない量が消費される
- APIキーを更新した際に、都度ワークフローも更新する必要がある
Orchestratorのアセット機能と『アセットを取得』アクティビティを利用すると、上記デメリットに対処できます。
1回のオートメーション実行で同じアセットに対して複数回『アセットを取得』アクティビティを実行する場合は、『アセットを取得』アクティビティの「キャッシュ ストラテジ」プロパティを「実行」と設定することで処理速度向上が見込まれます。1つのジョブ実行につき、初回のみOrchestratorに通信してアセット取得し、後はそのジョブ実行完了までアセットをキャッシュするためです。
クリップボードの過去履歴を確認したい
Windows 10 や Windows 11の場合、「Win + v」キーを押下することでクリップボード履歴を確認できます。
また『ステータスを報告』アクティビティに抽出した文字列を設定することでも、UiPath Assistantから有人実行した場合の「実行中」タブから履歴を確認できます。
日本語を抽出した際の、文字と文字の間に入る半角スペースを削除したい
2024年10月8日情報:Extended Languages OCRの改善により、最初から半角スペースが混ざらなくなりました。
正規表現を利用することで、パターンに一致する半角スペースを削除できます。
ただし正規表現に一致すればあるべき半角スペースであっても削除してしまうので、完璧ではありません。
- 全角文字間の半角スペースを削除する例
Regex.Replace(str_result, "([^(\x01-\x7E)]) ([^(\x01-\x7E)])", "$1$2")
- 平仮名、片仮名、または漢字間のスペースを削除する例
Regex.Replace(input, "(\p{IsHiragana}|\p{IsKatakana}|\p{IsCJKUnifiedIdeographs})\s+(\p{IsHiragana}|\p{IsKatakana}|\p{IsCJKUnifiedIdeographs})", "$1$2")
Regex
は宣言されていないというエラーが表示される場合には、Regex.Replace
の先頭にSystem.Text.RegularExpressions.
を追記すると解消します。
『テキストの一致を確認』アクティビティを利用し、正規表現に一致するスペースがあるかぎり『繰り返し』アクティビティで置換し続けます。
動画からもテキスト抽出&コピーできるようにしたい
『画面上で指定』アクティビティは、UI要素の選択完了後にその領域の画像を取得します。動画から文字抽出する場合、選択完了した(=画像取得する)タイミングではその場所の文字が変更していた、ということもあります。
この問題は、最初にスクリーンショットを取得してスクリーンショットからテキストを抽出することで解消できます。
Windows 10やWindows 11に標準搭載されているSnipping Tool(Win + Shift + s キー押下や、print screenキー押下で利用できる)は、スクリーンショットをクリップボードに保存します。クリップボードから画像を取得するローカルトリガー実装方法を考えました。
- 方法1:『プロセス終了トリガー』アクティビティを利用し、Snipping Tool終了をきっかけにクリップボードから画像を取得する。私のパソコンでは、『プロセス終了トリガー』に以下のプロセスを設定すると動作しました
- Windows 10 の場合:
ScreenClippingHost.exe
- Windows 11 の場合:
SnippingTool.exe
- Windows 10 の場合:
- 方法2:『ホットキー トリガー』アクティビティを利用し、画面キャプチャツールを起動するショートカットキー(たとえば Win + Shift + s )押下をきっかけに、クリップボードに画像が保管されるまで1秒の『待機』アクティビティを繰り返す。クリップボードに画像があることを確認したら、画像を取得する
クリップボード内の画像を操作するには、System.Windows.Forms.Clipboardクラスの以下メソッドが便利です。
- クリップボードに画像があるか判定する:Clipboard.ContainsImage()
- クリップボードから既存画像をクリアする:Clipboard.Clear()
- クリップボードから画像を取得するClipboard.GetImage()
フォーム開発時のTips
フォームを作りこんでみた時に、確認した設定例です。
フォームのコンポーネントを特定の条件で表示する例
歯車アイコンをクリックし、編集画面を開きます。
- 条件 > シンプル > ラジオの値が「hoge」の時に「このコンポーネントを表示」をTrueにする
- 条件 > シンプル > ラジオの値が「」(←何も入力しないまま)の時に「このコンポーネントを表示」をFalseにする
- 条件 > 高度な条件 > JavaScriptで
show = (data.radio_xxxx.length > 0);
のように指定する - 条件 > 高度な条件 > JavaScriptで
show = (data.radio_xxxx == 'hoge' || data.radio_xxxx == 'fuga');
のように指定する - 条件 > 高度な条件 > JSON論理で
{"!==": [{"var": "data.radio_xxxx"},""]}
のように指定する- JsonLogicについて:JavaScript Powered Forms and Form.io SDK、JsonLogic
フォームのコンポーネントのプロパティを特定の条件で変更する例
歯車アイコンをクリックし、編集画面を開きます。
- 論理で、以下のように設定する
- トリガー
- 種類:シンプル
- 条件として使用するフォームコンポーネント:ラジオ
- 条件として使用する値:hoge
- アクション
- 種類:プロパティ
- コンポーネントのプロパティ:ラベル
- Text:
hogeをスタート
- トリガー
- 論理で、以下のように設定する
- トリガー
- 種類:JavaScript
- テキスト領域:
result = (data.textArea_xxxx.substring(0,10) == 'ただいま処理中・・・');
- アクション
- 種類:プロパティ
- コンポーネントのプロパティ:無効
- ステートを設定:True
- トリガー
フォームのコンポーネントの値を変更する例
- ワークフロー内で『フォームの値を設定』アクティビティを利用する
- 編集画面の論理タブで、以下のように設定する
- トリガー
- 種類:イベント
- イベント名:ボタンコンポーネントのボタンイベントに設定した値
- アクション
- 種類:値
- 値 (JavaScript):
value = 'ただいま処理中・・・@' + (new Date()).toLocaleString();
- トリガー
フォームのコンポーネントにアイコンをつける例
歯車アイコンをクリックし、編集画面を開きます。
- 表示 > 左/ 右アイコンに「fa fa-play」や「fa fa-info」のように、先頭にfa を付けて指定する
- アイコン一覧:Find the Perfect Icon for Your Project | Font Awesome
- ボタンのプロパティ説明:Button Component
非表示にしてもデータを維持する例
既定値ではコンポーネントを非表示にするとデータがクリアされますが、クリアしないよう設定することもできます。
スパナアイコンの「JSONを編集」画面を開きます。
- 該当コンポーネントの設定がJSON形式で表示されるので、
"clearOnHide": false
を追記する- JSON構文に複数データがある場合は
,
で区切り、最終データの末尾には,
を入れない。json構文チェックなどで、フォーマットが正しいか確認できる - 「Container」コンポーネント内に入っている場合には、「Container」コンポーネントと該当コンポーネントの両方に「clearOnHide」プロパティを設定する
- その他の設定可能なプロパティ:Components JSON Schema
- JSON構文に複数データがある場合は