Vision API Pythonクライアントライブラリを少し深堀りする(BatchAnnotateFiles編)

本記事では、『Vision API Pythonクライアントライブラリを少し深堀りする(BatchAnnotateImages編)』に続いて、Vision APIのPythonクライアントライブラリを利用した、PDFファイルなどから特徴検出を行う同期メソッド(BatchAnnotateFilesに対応するメソッド)の使い方について少し深堀します。

<<お知らせ(2020/10/05)>>
Vision API Pythonクライアントライブラリの新バージョン(v2.0.0)が2020年9月29日付でリリースされています。本記事の内容はv1.0.0をもとに書いていますので、v2.0.0とは異なる内容、V2.0.0では動作しないコードを含んでいます。


【目次】

[1]はじめに

本記事は、前回の記事『Vision API Pythonクライアントライブラリを少し深堀りする(BatchAnnotateImages編)』に続いて、PDFファイルなどから特徴検出を行う同期メソッド(BatchAnnotateFilesに対応するメソッド)について、少し深堀します。

Vision APIのPythonクライアントライブラリのパッケージ内容や、画像から特徴検出を行う同期メソッド(BatchAnnotateImagesに関連するメソッド)などについては、前回の記事を参照してください。

なお、前回の記事と同様に、以下のようにgoogle.cloud.visionパッケージをインポートして、ImageAnnotatorClientをインスタンス化したとしてサンプルを書いています。
from google.cloud import vision
client = vision.ImageAnnotatorClient()

[2]BatchAnnotateFilesメソッド

ImageAnnotatorサービスのBatchAnnotateFilesメソッドは、PDFなどのファイルから特徴抽出を行う同期メソッドです。

主な特徴は以下の通りです。
  • 1つのファイルに複数のページやフレームを含めることが出来るファイル形式から特徴検出します。具体的には、PDF、TIFF、アニメーションGIFファイルです。
  • 検出できる特徴は、Vision APIの機能リストにあるものです。一つの画像に対して同時に複数の特徴を検出できます。
  • PDFファイルのOCRについては、ページ内の画像だけでなく、テキスト情報も検出対象になっているようです。(画像がないPDFファイルも扱えます。)
  • 送信できるのは1ファイルのみですが、ファイル内にあるページのうち、5ページ分まで同時に処理できます。(これはファイル全体のページ数の制限ではなく、特徴検出対象とするページ数の制限です。)
  • 同期処理のため、メソッドの戻り値として指定した特徴検出の結果を得ることができます。このため、非同期版に比べて手軽に利用できます。

BatchAnnotateFilesについては『Vision APIのBatchAnnotateFilesメソッド(ファイルからの同期特徴抽出)を少し深堀りする』を参照してください。

このBatchAnnotateFilesに対応するImageAnnotatorClientクラスのメソッドは、batch_annotate_filesのみです。
BatchAnnotateImagesと違って、簡易に利用できるメソッドは用意されていません。

ImageAnnotatorClientクラスのメソッド構成

[3]batch_annotate_filesメソッド

ImageAnnotatorClientクラスのbatch_annotate_filesの定義は以下のように定義されています。
batch_annotate_files(requests,parent=None,retry=google.api_core.gapic_v1.method.DEFAULT,timeout=google.api_core.gapic_v1.method.DEFAULT,metadata=None)

引数
  • requests:(必須)
    • vision.types.AnnotateFileRequest型(ディクショナリもOK)のリストを指定します。
    • リストで指定しますが、リストの要素は1つの.AnnotateFileRequestしか設定できません。(複数のAnnotateFileRequestは指定できません。)
  • parent:(オプション)
  • retry:(オプション)
    • リトライ設定
  • timeout:(オプション)
    • タイムアウトまでの秒数
  • metadata:(オプション)
    • gRPCのmetadataを追加できるようです。

戻り型
  • vision.types.BatchAnnotateFilesResponse

[4]リクエストデータの表現方法

ImageAnnotatorサービスのBatchAnnotateFilesメソッドの引数(リクエストデータ)は、BatchAnnotateFilesRequestです。

関連するデータ構造を図で表してみます。
Vision API:BatchAnnotateFilesRequestのデータ構造


クライアントライブラリのメソッドの引数は、BatchAnnotateFilesRequestではなく、AnnotateFileRequestのリストとなっており、クライアントライブラリが内部でProtocol Buffersのデータ型(BatchAnnotateFilesRequest)を構成して通信してくれます。

クライアントライブラリのAnnotateImageRequestの場合と同様に、クライアントライブラリに渡すデータは、
  • Protocol Buffersのデータ型で直接表現する(vision.typesを利用する)方法
  • Pythonのディクショナリ(dict)で表現する方法
の二通りがあります。これらを混在させることもできます。

以下では、AnnotateFileRequestの要素の作り方を見ていきます。

AnnotateFileRequestは4つのフィールドを持ちます。
  • input_config:(必須)解析したい1つのファイルを指定します。
  • features:(必須)検出したい特徴タイプを指定します。(複数指定可能)
  • image_context:(オプション)検出したい特徴タイプ固有のパラメータ
  • pages:(オプション)検出対象ページを指定します。(実用上は必須かも)

このうち、featuresとimage_contextはAnnotateImageRequestと同じです。
AnnotateImageRequestとの違いは、AnnotateImageRequestが画像情報をImage型で表現したのに対して、AnnotateFileRequestはファイル情報をInputConfig型で表現します。加えて、AnnotateFileRequestには、ファイル内の対象ページを指定するpagesフィールドがあります。

(1)InputConfig:ファイルのバイナリデータを送信する場合

contentフィールドに、ファイルのバイナリデータを設定します。

例えば、以下のようにファイルのデータをbinary_content変数に読み込んだとします。
import io
with io.open('ファイルのパス', 'rb') as image_file:
  binary_content = image_file.read()

指定できるファイル形式は、PDF、TIFF、アニメーションGIFです。
ファイル形式に対応したMIMEタイプをmime_typeフィールドに設定する必要があります。
  • PDF=application/pdf
  • TIFF=image/tiff
  • GIF=image/gif

もし、mime_typeフィールドを設定しなかった場合は、以下の例外がスローされます。
  • InvalidArgument: 400 MimeType is required.
また、上記以外のMIMEタイプを指定すると以下の例外がスローされます。
  • InvalidArgument: 400 At this time, the only MIME types supported are 'application/pdf', 'image/gif' and 'image/tiff'.

以下のコード例は、PDFファイルの場合としてmime_typeを設定しています。

①vision.typesで表現

input_config = vision.types.InputConfig()
input_config.content = binary_content
input_config.mime_type = 'application/pdf'

フィールドの値は、オブジェクト作成時にキーワード引数として渡すこともできます。
input_config = vision.types.InputConfig(content = binary_content,mime_type = 'application/pdf')

この書き方は、Googleのガイドのサンプルにもある書き方で、よく用いられる書き方のようです。

②ディクショナリで表現

InputConfigオブジェクトのフィールドと値をそのまま「キー:値」のペアで表現します。
input_config = {'content': binary_content, 'mime_type': 'application/pdf'}

(2)InputConfig:Google Cloud Storageのパスを指定する場合

Google Cloud Storage上のファイルパスを、GcsSourceオブジェクトのuriフィールドに、 gs://bucket_name/object_nameの形式で設定し、これをInputConfigのgcs_sourceに設定します。
なお、Image型と違って、Web上で公開されているファイルのURLを指定することはできません。

①vision.typesで表現

input_config = vision.types.InputConfig()
input_config.gcs_source.uri = 'gs://bucket_name/object_name'
input_config.mime_type = 'application/pdf'

この場合、キーワード引数で指定する方法は以下のようになります。
input_config = vision.types.InputConfig(
  gcs_source = vision.types.GcsSource(uri='gs://bucket_name/object_name'),
  mime_type = 'application/pdf')

②ディクショナリで表現

オブジェクトのネストも簡単に表現できます。
input_config ={'gcs_source':{'uri':'gs://bucket_name/object_name'}, 'mime_type':'application/pdf'}

(3)Feature:検出したい特徴の指定

vision.enums.Feature.Typeを利用してFeatureを作ります。(以下はTEXT_DETECTIONの例です。)

①vision.typesで表現

ft = vision.types.Feature()
ft.type = vision.enums.Feature.Type.TEXT_DETECTION

キーワード引数で指定する方法は以下のようになります。
ft = vision.types.Feature(type = vision.enums.Feature.Type.TEXT_DETECTION)

必要に応じて、max_resultsやmodelも指定します。
例えば、顔検出で顔を1つのみ検出したい場合はmax_resultsに1を指定します。 モデルを指定する必要がある場合については、『Vision APIのBatchAnnotateImagesメソッド(画像からの同期特徴抽出)を少し深堀りする/[5]モデルのバージョンアップへの対応』も参考にしてください。

ft = vision.types.Feature()
ft.type = vision.enums.Feature.Type.FACE_DETECTION
ft.max_results = 1
ft.model = "builtin/stable"

キーワード引数で指定する方法は以下のようになります。
ft = vision.types.Feature( type=vision.enums.Feature.Type.FACE_DETECTION, max_results = 1, model = "builtin/stable")

②ディクショナリで表現

ft = {'type': vision.enums.Feature.Type.TEXT_DETECTION}

必要に応じて、max_resultsやmodelも指定します。
vision.typesの例は、以下のように書けます。
ft = {'type': vision.enums.Feature.Type.FACE_DETECTION, 'max_results':1,  'model':"builtin/stable"}

(4)ImageContext:特徴タイプ固有のパラメータを設定する場合

特徴タイプによっては、パラメータを設定できるものがあります。クロップヒントの設定例をみてみます。

①vision.typesで表現

im_context = vision.types.ImageContext()
im_context.crop_hints_params.aspect_ratios.extend([1.77])
このようにも書けます。
im_context = vision.types.ImageContext(
        crop_hints_params=vision.types.CropHintsParams(aspect_ratios=[1.77]))

②ディクショナリで表現

im_context = {'crop_hints_params': {'aspect_ratios':[1.77]} }

(5)pagesフィールド:検出対象ページの指定

ファイル内の特徴検出したいページをpagesフィールドで指定します。

5ページ以上指定すると以下の例外がスローされます。
  • InvalidArgument: 400 At most 5 pages in one call please.

pagesを省略すると、デフォルトの動作として、ファイルの最初の1ページから5ページまで(最大5ページ)が検出対象となります。

ページは、最初のページが1となり、次のページを2として指定します。
また、マイナスのページ指定もできます。例えば、-1を指定すると、最後のページを指定したことになり、-2を指定すると、最後のページの前のページを指定したことになります。
(但し、マイナスのページ指定を指定した場合、後述のレスポンスデータのpage_numberフィールドには、マイナスのページ番号ではなく、通しページ番号が設定されます。)

以下はpagesを指定する場合の例です。
  • 3ページ目のみ検出対象にする例
    • [3]
  • 1ページ目、3ページ目、5ページ目、最後から2ページ目、最後のページを検出対象にする例
    • [1,3,5,-2,-1]

pagesの指定と、レスポンスの関係については、「[6]レスポンスデータの扱い/(2)リクエスト時のページ指定と結果ページの関係」を参照してください。

[5]batch_annotate_filesの呼び出し

これまで、AnnotateFileRequestのデータ要素を作成する方法を見てきました。これらをAnnotateFileRequestのオブジェクトの各フィールドに設定すれば、batch_annotate_filesメソッドをを呼び出すことができます。

batch_annotate_filesメソッドの引数は、AnnotateFileRequestのリストになっているため、一見すると複数のリクエストを送信できそうに思えますが、1つのリクエスト(ファイル)に制限されています。(但し、ファイル内の5ページまで検出できます。)
もし、複数のリクエストを送信すると、以下の例外がスローされます。
  • InvalidArgument: 400 Right now only one AnnotateFileRequest in BatchAnnotateFilesRequest is supported.

以下では、Googleのガイドにあるサンプルを利用させてもらいます。

このサンプルでは、30ページあるPDFファイルの中から、1ページ、2ページ、最後のページ(-1)の合計3ページに対して、DOCUMENT_TEXT_DETECTIONを実行します。

このガイドの下の方にあるPDFファイルは、Cloud Storageのパス(gs://cloud-samples-data/vision/document_understanding/custom_0773375000.pdf)から利用したり、リンクの右クリックでダウンロードすることもできるようです。
(ただし、このファイルはノイズや手書きがある英語文書であり、日本語抽出のサンプルには適していませんが、ここではAPIの使い方のサンプルとして利用させてもらいます。)

以下のサンプルは、DOCUMENT_TEXT_DETECTIONのみのサンプルになっていますが、特徴タイプは複数指定できます。また特徴固有のパラメータを設定できます。これらのコードサンプルは『Vision API Pythonクライアントライブラリを少し深堀りする(BatchAnnotateImages編)/[6]annotate_imageメソッド』を参考にしてください。

(1)ローカルに保存されたファイルを使用する

①vision.typesで表現

ベタな書き方です。
req = vision.types.AnnotateFileRequest()
# 参照:(1)InputConfig:ファイルのバイナリデータを送信する場合
req.input_config.content = binary_content
req.input_config.mime_type = 'application/pdf'
# 参照:(3)Feature:検出したい特徴の指定を参照
ft = req.features.add()
ft.type = vision.enums.Feature.Type.DOCUMENT_TEXT_DETECTION
# 参照:(5)pagesフィールド:検出対象ページの指定
req.pages.extend([1, 2, -1])

# batch_annotate_files呼び出し。リクエストがリストであることに注意。
batch_response = client.batch_annotate_files([req])

キーワード引数で指定する方法は以下のようになります。
req = vision.types.AnnotateFileRequest(
    input_config = vision.types.InputConfig(content = binary_content,mime_type = 'application/pdf'),
    features = [vision.types.Feature(type = vision.enums.Feature.Type.DOCUMENT_TEXT_DETECTION)],
    pages = [1, 2, -1]
)

# batch_annotate_files呼び出し。リクエストがリストであることに注意。
batch_response = client.batch_annotate_files([req])

②ディクショナリで表現

ガイドのサンプルはディクショナリで表現しているので、ほぼ同じです。
req = {
    'input_config': {'content': binary_content, 'mime_type': 'application/pdf'},
    'features': [{'type': vision.enums.Feature.Type.DOCUMENT_TEXT_DETECTION}],
    'pages': [1, 2, -1]
}

# batch_annotate_files呼び出し。リクエストがリストであることに注意。
batch_response = client.batch_annotate_files([req])

(2)Cloud Storage 上のファイルを使用する

以下の例では、gs_pathは”gs://bucket_name/object_name”形式のGoogle Cloud Storage上のパスとします。

①vision.typesで表現

ベタな書き方です。
req = vision.types.AnnotateFileRequest()
# 参照:(2)InputConfig:Google Cloud Storageのパスを指定する場合
req.input_config.gcs_source.uri = gs_path
req.input_config.mime_type = 'application/pdf'
# 参照:(3)Feature:検出したい特徴の指定を参照
ft = req.features.add()
ft.type = vision.enums.Feature.Type.DOCUMENT_TEXT_DETECTION
# 参照:(5)pagesフィールド:検出対象ページの指定
req.pages.extend([1, 2, -1])

# batch_annotate_files呼び出し。リクエストがリストであることに注意。
batch_response = client.batch_annotate_files([req])

キーワード引数で指定する方法は以下のようになります。
req = vision.types.AnnotateFileRequest(
    input_config =  vision.types.InputConfig(
        gcs_source = vision.types.GcsSource(uri=gs_path),
        mime_type = 'application/pdf'),
    features = [vision.types.Feature(type = vision.enums.Feature.Type.DOCUMENT_TEXT_DETECTION)],
    pages = [1, 2, -1]
)

# batch_annotate_files呼び出し。リクエストがリストであることに注意。
batch_response = client.batch_annotate_files([req])

②ディクショナリで表現

ガイドのサンプルはディクショナリで表現しているので、ほぼ同じです。
req = {
    'input_config':{
        'gcs_source': {'uri': gs_path},
        'mime_type': 'application/pdf'},
    'features': [{'type': vision.enums.Feature.Type.DOCUMENT_TEXT_DETECTION}],
    'pages': [1, 2, -1]
}

# batch_annotate_files呼び出し。リクエストがリストであることに注意。
batch_response = client.batch_annotate_files([req])

[6]レスポンスデータの扱い

(1)概要

ImageAnnotatorサービスのBatchAnnotateFilesメソッドの戻り値(レスポンスデータ)は、BatchAnnotateFilesResponseです。

関連するデータ構造を図で表してみます。

Vision API:BatchAnnotateFilesResponseのデータ構造

batch_annotate_filesメソッドの戻り値も、vision.types.BatchAnnotateFilesResponseです。

BatchAnnotateFilesResponseのresponsesフィールドには、AnnotateFileRequestに対応する検出結果(AnnotateFileResponse)が格納されます。
実際には、BatchAnnotateFilesメソッドは、1つのAnnotateFileRequestしか受け付けないため、結果としてAnnotateFileResponseも1つです。

AnnotateFileResponseのresponsesフィールドには、検出対象のページ毎にAnnotateImageResponse型で検出結果が設定されています。
(AnnotateImageResponseはBatchAnnotateImagesの結果と同じ型です。)

AnnotateImageResponseに設定されるフィールドは、特徴タイプに応じて決まります。特徴タイプ毎の処理やフィールドについては、ボリュームも大きいことから、別の記事で書きたいと思います。

(追記:2020/10/27)
OCR関連のレスポンスデータ(fulTextAnnotationとtextAnnotatins)については、以下の記事を書きましたので参考にしてください。

BatchAnnotateFilesResponseの各フィールドやエラーについては、『Vision APIのBatchAnnotateFilesメソッド(ファイルからの同期特徴抽出)を少し深堀りする/[3]レスポンス:BatchAnnotateFilesResponse』を参照してください。

また、リクエストはvision.typesとディクショナリのどちらかで表現することができましたが、レスポンスはvision.typesのProtocol Buffersのクラスとして返却されます。

(2)リクエスト時のページ指定と結果ページの関係

PDFファイルのページ内容と構成と処理したいページが明確に分かっている場合は特に問題ありませんが、そうでない場合は、リクエスト時のページと結果ページの関係を把握したうえで、アプリケーションの仕様を考える必要があるかもしれません。
(特に、解析対象のページ指定をユーザが任意に行えるような場合。)

というのは、BatchAnnotateImagesメソッドではリクエストとレスポンスデータが1対1対応していますが、BatchAnnoateFilesメソッドは、そうならないケースもあるため、何らかの対応が必要になる場合があります。

以下では、リクエスト時のpages指定と、結果のtotal_pages、page_numberの関係を少し整理して、具体的なケースを見てみます。

リクエスト時には, AnnotateFileRequestで、解析対象ページを省略(先頭5ページまで検出)するか、pagesフィールドで検出対象ページを指定できます。

一方、ページ毎の検出結果は、AnnotateFileResponseのresponsesフィールド内のAnnotateImageResponseオブジェクトで得られます。
このAnnotateImageResponseオブジェクトが、どのページの検出結果なのか?は、AnnotateImageResponseのcontextフィールド(ImageAnnotationContext型)のpage_numberフィールドより得られます。

そして、AnnotateFileResponseには total_pagesフィールドがあり、ファイルの全ページ数が得られます。

①リクエスト時のマイナス指定のページ番号と結果のページ番号

まず、AnnotateFileResponseのページ情報を表示する簡単な関数を用意します。
def print_annotate_file_response_page_info( file_response ):
    # total_pageの表示
    print("total_pages={}".format( file_response.total_pages ))
    # AnnotateFileResponseのerror情報の表示
    print("error:code={}, message={}".format(
        file_response.error.code, file_response.error.message ))
    # 検出結果の数(responsesの要素数)を表示
    print("len(responses)={}".format( len(file_response.responses) ))
    # ページ毎の検出結果のページ番号、エラー情報を表示
    for i, ires in enumerate( file_response.responses ):
        print("response[{}] = page_number={} / error:code={}, message={}".format(
            i, ires.context.page_number, ires.error.code, ires.error.message))

続いて、「[5]batch_annotate_filesの呼び出し/(2)Cloud Storage 上のファイルを使用する」のコードを利用して、GoogleガイドのサンプルPDFファイル(gs://cloud-samples-data/vision/document_understanding/custom_0773375000.pdf)を呼び出した結果を表示してみます。

なお、このPDFファイルは、30ページあり、これに対してリクエストのpagesフィールドに[1,2,-1](1ページ、2ページ、最後のページ)を指定した場合の検出結果は以下のようになります。

(コード)
req = {
    'input_config':{
        'gcs_source': {'uri': 'gs://cloud-samples-data/vision/document_understanding/custom_0773375000.pdf'},
        'mime_type': 'application/pdf'},
    'features': [{'type': vision.enums.Feature.Type.DOCUMENT_TEXT_DETECTION}],
    'pages': [1, 2, -1]
}
batch_response = client.batch_annotate_files([req])
print_annotate_file_response_page_info(batch_response.responses[0])

(結果)
total_pages=30
error:code=0, message=
len(responses)=3
response[0] = page_number=1 / error:code=0, message=
response[1] = page_number=2 / error:code=0, message=
response[2] = page_number=30 / error:code=0, message=

結果を見ると、リクエストで指定した最後のページ(-1で指定)は、結果ではページ番号になっていることが分かります。

このことから、リクエスト時のpagesで指定したページの結果をAnnotateFileRequestから取り出す関数は、例えば、以下のように書けます。
def get_image_response_by_request_page(req_page, file_response):
    """リクエスト時に指定したページの結果がAnnotateFileResponseにあれば返す"""
    if req_page < 0:
        req_page = file_response.total_pages + req_page + 1
    for ires in file_response.responses:
        if ires.context.page_number == req_page:
            return ires
    return None

これを利用して、上記サンプルの実行結果(batch_response)から、リクエスト(pages)で指定したページの検出結果を取り出すことができます。

(サンプルコード)
for req_page in [1,2,-1]:
    ires = get_image_response_by_request_page(req_page, batch_response.responses[0])
    if ires is None:
        print("Not found! req_page={}".format(req_page))
    else:
        print("req_page={}, page_number={}".format(req_page, ires.context.page_number))

(実行結果)
req_page=1, page_number=1
req_page=2, page_number=2
req_page=-1, page_number=30

②リクエスト時のページ番号の並びと結果の並び順

先のサンプルを利用して、pages指定を、[2,1, -1, -2] (2ページ、1ページ、最後のページ、最後から2ページ目)として呼び出して、結果を表示してみます。

(コード)
req = {
    'input_config':{
        'gcs_source': {'uri': 'gs://cloud-samples-data/vision/document_understanding/custom_0773375000.pdf'},
        'mime_type': 'application/pdf'},
    'features': [{'type': vision.enums.Feature.Type.DOCUMENT_TEXT_DETECTION}],
    'pages': [2,1, -1, -2]
}
batch_response = client.batch_annotate_files([req])
print_annotate_file_response_page_info(batch_response.responses[0])

(結果)
total_pages=30
error:code=0, message=
len(responses)=4
response[0] = page_number=1 / error:code=0, message=
response[1] = page_number=2 / error:code=0, message=
response[2] = page_number=29 / error:code=0, message=
response[3] = page_number=30 / error:code=0, message=

結果はリクエスト時のページ順とは異なり、ページの昇順で並んでいます。

これをみると、リクエスト前に予めページ指定をソートするとか、リクエスト時のページに対応するレスポンスを対応付ける処理が必要な場合があることがわかります。(例えば、先のget_image_response_by_request_page関数のような対応するページを探す処理など)

ただし、マイナスのページ番号を含むページ指定をリクエスト前にソートする場合は、リクエスト前に総ページ数を把握しておく必要があると思います。

③リクエスト時に重複したページを指定した場合

先のサンプルを利用して、pages指定を、[1,1, 30,-1](1ページ、1ページ、30ページ、最後のページ)として呼び出して、結果を表示してみます。
ここで、1ページは明らかに重複していますが、サンプルファイルは30ページしかないため、実際には、-1も30ページを指しており、これも重複しています。

(コード)
req = {
    'input_config':{
        'gcs_source': {'uri': 'gs://cloud-samples-data/vision/document_understanding/custom_0773375000.pdf'},
        'mime_type': 'application/pdf'},
    'features': [{'type': vision.enums.Feature.Type.DOCUMENT_TEXT_DETECTION}],
    'pages': [1,1, 30,-1]
}
batch_response = client.batch_annotate_files([req])
print_annotate_file_response_page_info(batch_response.responses[0])

(結果)
total_pages=30
error:code=0, message=
len(responses)=2
response[0] = page_number=1 / error:code=0, message=
response[1] = page_number=30 / error:code=0, message=

エラーはありませんが、結果は重複したページは取り除かれています。結果として、リクエストしたページ数と結果のページ数が異なることになります。

1と1のような明らかに重複したものはユニーク化が簡単に行えますが、30と-1のように、総ページ数がわからないと同じページかどうかわからない場合もあります。送信前に総ページ数を把握して重複を取り除くか、レスポンスから重複を把握して処理するなどの対処が必要になるかもしれません。

④リクエスト時に存在しないページを指定した場合

先のサンプルを利用して、pages指定を、[1,50, -1,-50](1ページ、50ページ、最後のページ、最後から50ページ目)として呼び出して、結果を表示してみます。
ここで、ファイルには30ページしかないので、50ページ目と-50ページ目は存在しません。

(コード)
req = {
    'input_config':{
        'gcs_source': {'uri': 'gs://cloud-samples-data/vision/document_understanding/custom_0773375000.pdf'},
        'mime_type': 'application/pdf'},
    'features': [{'type': vision.enums.Feature.Type.DOCUMENT_TEXT_DETECTION}],
    'pages': [1,50, -1,-50]
}
batch_response = client.batch_annotate_files([req])
print_annotate_file_response_page_info(batch_response.responses[0])

(結果)
total_pages=30
error:code=0, message=
len(responses)=2
response[0] = page_number=1 / error:code=0, message=
response[1] = page_number=30 / error:code=0, message=

結果を見ると、存在しないページの結果は含まれておらず、存在するページの検出結果のみが格納されています。また、エラーもありません。

これに対して、リクエスト前にページの存在確認を行うか、レスポンスから該当ページがないことへの対処を行う必要があるかもしれません。

ちなみに、上記の例はAnnotateFileResponseが1つ以上ある例ですが、1つもAnnotateFileResponseがない(responsesの要素が0)になるリクエストの場合は、例外がスローされます。
例えば、pages指定を[50,-50]とすると、以下の例外がスローされます。
  • InvalidArgument: 400 No pages found.


コメント

このブログの人気の投稿

マイナンバーカードの電子証明書(公的個人認証サービス)にJava APIでアクセスする

Google Document AIで画像から表形式データを抽出する(Vision API OCRとの違い)

VirtualBoxのスナップショット機能