Slide 1

Slide 1 text

HCL Nomad と OCR 添付された画像ファイルに存在する文字 を抽出しよう 中野晴幸 (Haruyuki Nakano) harunakano.blogspot.com (blog) @harunakano (twitter)

Slide 2

Slide 2 text

取り組んだきっかけ • NotesHTTPRequest と NotesJSONNavigator の検証 • HCL Nomad(旧DMA)でも動くアプリを作りたい • HCL Master 小野様からのヒント ➢「LINE に投稿した画像を OCR できますよ」 たぶん LINE + Node-RED + Google Cloud Vision API で実装?

Slide 3

Slide 3 text

OCR とは • Optical Character Recognition/Reader 光学式文字認 識 • 印刷テキストや手書き文字を読み取り、デジタルの文字コードに 変換する技術 • 近年のAI技術により変換の精度が格段に上がった(気がする)

Slide 4

Slide 4 text

Google Cloud Vision API • 登録は必要だが 1000 回/月の問い合わせまでは無料 • API Key の取得が必要 https://cloud.google.com/vision/

Slide 5

Slide 5 text

概要 1. 文書に添付した写真をローカルへ保存 2. 写真をテキスト化する(Base64エンコード) 3. リクエストをJSON形式で作る 4. リクエストを投げてレスポンスを得る 5. レスポンスからOCRの結果を得て表示する 6. ローカルの写真を削除する 上記を写真の数だけ繰り返します

Slide 6

Slide 6 text

ローカルへ保存と言うけれど…どこ? • Windows でも iOS でも読み書きできる保存先と言えば…? ✓Notes データディレクトリ • Notes データディレクトリの場所を調べるには…? ✓notes.ini の “Directory” ➢iOS では notes.ini から Notes プログラムディレクトリが取得できな い!? Dim ss As New NotesSession dataDir$ = ss.Getenvironmentstring("Directory", True )

Slide 7

Slide 7 text

ファイルをローカルへ保存する • Body フィールドに添付したすべてのファイルが対象です Dim rtitem As NotesRichTextItem Set rtitem = doc.Getfirstitem("Body") ForAll o In rtitem.Embeddedobjects If o.Type = EMBED_ATTACHMENT Then filepath = dataDir & dlm & o.Source o.ExtractFile filepath ‘ここでOCR処理を行う Kill filepath End If End ForAll

Slide 8

Slide 8 text

【メモ】区切り文字に注意 • フォルダ名とファイル名の区切り文字がOSによって異なります • Windows: ¥ 円マーク(バックスラッシュで表示されることも) • iOS: / スラッシュ Dim ss As New NotesSession Dim platform$, dlm$ platform = LCase( ss.platform ) If platform = "ios" Then dlm = "/" ElseIf platform = "windows/32" Then dlm = "¥" End If

Slide 9

Slide 9 text

写真をテキスト化というけれど… • 写真ファイルのまま送れないの? • 写真ファイル(バイナリ)のまま送る場合、バイト配列に変換しますが、 LotusScript の配列の制限によりサイズが 32KB までに制限される ため、ちょっと大きなサイズのファイルが扱えません • どうやってテキスト化するの? • ファイルを BASE64 エンコードするサンプルが OpenNTF にあります ※ “openntf readencoded” でググると LotusScript のコードが見つかります

Slide 10

Slide 10 text

どうして JSON なの? • Slack へファイルを送信する場合は MIME 形式で要求しなけれ ばなりませんが、Google Cloud Vision API では JSON で の要求をサポートしています ※ちなみに現状 slack は BASE64 エンコードをサポートしていません

Slide 11

Slide 11 text

API へリクエストを投げる • 次の JSON にある EncodedText の部分を BASE64 エン コードしたものに置換してPOSTします { "requests": [ { "image": { "content": "EncodedText" }, "features": [ { "type": "DOCUMENT_TEXT_DETECTION" } ] } ] } Dim ss As New NotesSession Dim req As NotesHTTPRequest Dim nav As NotesJSONNavigator Set req = ss.Createhttprequest() json$ = |{“requests”: [{“image”:{“content”:”…”},…}]}| req.Timeoutsec = 60 req.Preferjsonnavigator = True req.Setheaderfield "Content-Type", "application/json" Set nav = req.Post( url, json )

Slide 12

Slide 12 text

【参考】リクエスト先のURL • (認証情報となる) API キーをURLへ埋め込みます https://vision.googleapis.com/v1/images:annotate?key=API Key

Slide 13

Slide 13 text

レスポンスを得る • テキスト抽出が成功すると、次のような JSON を戻します • description の値に、すべてのテキストがあります { "responses": [ { "textAnnotations": [ { "description": "OCR Text" } ] } ] } (他にも結果が入ってそうな箇所があるが…)

Slide 14

Slide 14 text

レスポンスからOCRの結果を得る(1) • 階層をたどる方法 { "responses": [ { "textAnnotations": [ { "description": "OCR Text" } ] } ] } Dim elm As NotesJSONElement Dim obj As NotesJSONObject Dim arr As NotesJSONArray Set elm = nav.Getelementbyname( "responses" ) Set arr = elm.Value Set elm = arr.Getfirstelement() Set obj = elm.Value Set elm = obj.Getelementbyname( "textAnnotations" ) Set arr = elm.Value Set elm = arr.Getfirstelement() Set obj = elm.Value Set elm = obj.Getelementbyname( "description" ) MessageBox elm.Value,,"OCR Text" 探したい値が配列の何番目にある かわからない場合に有効です

Slide 15

Slide 15 text

レスポンスからOCRの結果を得る(2) • ポインタを指定する方法 { "responses": [ { "textAnnotations": [ { "description": "OCR Text" } ] } ] } Dim elm As NotesJSONElement pointer$ = "/responses/0/textAnnotations/0/description" Set elm = nav.Getelementbypointer( pointer ) MessageBox elm.Value,,"OCR Text" 探したい値が配列上のどの位置にある か確定している場合に有効です

Slide 16

Slide 16 text

Demo

Slide 17

Slide 17 text

まとめ

Slide 18

Slide 18 text

1. OSの違いに注意 Windows iOS フォルダの区切り文字 円マーク スラッシュ Notes.ini から 取得可能なディレクトリ プログラム・ディレクトリ、 データ・ディレクトリ データ・ディレクトリ

Slide 19

Slide 19 text

2. タイムアウト • Notes クライアント:問題なし • Nomad: ときどきタイムアウトする • NotesHTTPRequest のTimeoutsec プロパティが効かない? • HCL Nomad の制限:10秒でタイムアウトする • Timeoutsec のデフォルト値は 30(秒)。プロパティへ 60 をセットしても iOS では 10 秒でタイムアウトする • タイムアウトすると、エラーコード:4846 「HTTP request timeout」となる • ネットワークの帯域幅や混雑状況の影響を受ける

Slide 20

Slide 20 text

3. 新機能の不具合(1) • 写真に写る文字数が少ない(=OCRからのレスポンスのサイズが小さ い)場合、Nomadでもうまくいく!? • 文字数が多い場合、エラーコード:4842 「Unable to Parse JSON string: Missing a name for object member: offset 1」でエラーと なる傾向があった ➢Notes 10.0.1 FP2、HCL Nomad 1.0.4 で不具合が解消\( ‘ω’)/

Slide 21

Slide 21 text

【参考】Notes 10.0.1 FP2 新機能の問題が多く修正されています! • DCONB8VMAV - NotesJSONNavigator is unable to parse JSON content > 64k • ASHEB95LFR - Unable to parse JSON string: Missing a name for object member, offset 1 • DCONB8F6JV - bad character conversion happening in NotesHTTPRequest response • ASHEB95LFR - NotesJSONNavigator unable to navigate a string which contains new lines and carriage returns • DCONBB2KNR - NotesJSONNavigator experiencing severe issues when parsing packages with empty string values • JCORBB2KWU - Unable to Post > 64K String in NotesHTTPRequest • DCONBB44T4 - Creating a NotesJSONNavigator from nulled response causes crash Limitations of NotesHTTPRequest and NotesJSONNavigator with future considerations https://www.ibm.com/support/pages/limitations-noteshttprequest-and-notesjsonnavigator-future-considerations

Slide 22

Slide 22 text

【参考】 HCL Nomad 1.0.4 • Notes 10.0.1 FP2 相当の修正が適用された(気がする) • 1.0.4 は現状 TestFlight でのみ提供 • アプリが変わりました! 1.0.3 まで ➢IBM Domino Mobile Apps 1.0.4 以降 ➢HCL Nomad

Slide 23

Slide 23 text

3. 新機能の不具合(2) • OCRからのレスポンス(JSON)に”Ⓡ”や”・”を含む場合にパース できない • NotesJSONNavigator へセットしようとすると、エラーコード:4842 「Unable to Parse JSON string: Missing comma or ‘}’ after object member. offset ??」でエラーとなる ➢Notes 10.0.1 FP2、Notes V11 Beta1、HCL Nomad 1.0.4 で不具合を確 認 ➢回避する方法あり〼(下表) エラーとなるコード 回避するコード set req = ss.CreateHTTPRequest() req.Preferstrings = True json = req.Post( url, body ) Set nav = ss.Createjsonnavigator( json ) ‘ここでエラー set req = ss.CreateHTTPRequest() req.Preferjsonnavigator = True Set nav = req.Post( url, body )

Slide 24

Slide 24 text

スライドの最後