Amazon Rekognition で画像に映っている物やテキストを検出する

Rekognition では、イメージやビデオ内で、見つかった物体、シーン、または概念などに、ラベルやタグをつけることで、画像に映り込んでいるものを情報として利用することができそうです。

Rekognition にあらかじめ登録されているものを検出できます。
Rekognitionは、物をラベルによって、分類し、祖先ラベルの階層分類を使用しています。
これは、親ラベルを使用して、関連するラベルのグループを作成したり、1 つ以上の画像内の類似ラベルを照会したりできます。
Webリクエストでのラベルによるクエリで、この機能を使って、面白い機能が作れそうです。

まずはAPIリファレンスです。

APIリファレンス

Rekognition  detect_labelsメソッド

機能

入力として提供されるイメージ (JPEG または PNG) 内の物(ラベル)を検出します。
これには、花、木、テーブルなどのオブジェクト、結婚式、卒業、誕生日パーティーなどのイベント、風景、夕方、自然などの概念が含まれます。

base64 でエンコードされたイメージのバイトまたは S3 オブジェクトの内のイメージへの参照を渡します。
AWS CLI を呼び出す場合、base64 でエンコードされたイメージバイトを渡すことはできません。
イメージは、PNG または JPEG 形式ファイルにする必要があります。

各オブジェクト、シーン、およびコンセプトについて、API は 1 つ以上のラベルを返します。
各ラベルには、オブジェクト名と、イメージにオブジェクトが含まれていることの信頼度が表示されます。
たとえば、入力イメージに灯台、海、岩があるとします。レスポンスには、オブジェクトごとに1つずつ、3つのラベルがすべて含まれます。
それに応じて、APIはラベルの配列を返します。 さらに、応答には方向補正も含まれます。 
オプションで、MinConfidenceを指定して、返されるラベルの信頼しきい値を制御できます。 
デフォルトは55%です。MaxLabelsパラメーターを追加して、返されるラベルの数を制限することもできます。

DetectLabelsは、インスタンスオブジェクトの配列内の共通オブジェクトラベルのインスタンスの境界ボックスを返します。
インスタンスオブジェクトには、画像上のラベルの場所用のBoundingBoxオブジェクトが含まれています。
また、バウンディングボックスが検出された信頼性も、検出されたラベルの階層分類も返します。

たとえば、検出された車にラベルcarが割り当てられ、Vehicle(その親)とTransportation(その祖父母)の2つの親ラベルがあります。 
応答は、ラベルの祖先のリスト全体を返します。各祖先は、応答内の一意のラベルです。
前の例では、Car、Vehicle、およびTransportationが応答で一意のラベルとして返されます。
これはステートレスAPI操作で、操作はデータを永続化しません。

この操作には、rekognition:DetectLabelsアクションを実行するためのパーミッションが必要です。

構文

{
   "Image": { 
      "Bytes": blob,
      "S3Object": { 
         "Bucket": "string",
         "Name": "string",
         "Version": "string"
      }
   },
   "MaxLabels": number,
   "MinConfidence": number
}

戻り値

{
   "LabelModelVersion": "string",
   "Labels": [ 
      { 
         "Confidence": number,
         "Instances": [ 
            { 
               "BoundingBox": { 
                  "Height": number,
                  "Left": number,
                  "Top": number,
                  "Width": number
               },
               "Confidence": number
            }
         ],
         "Name": "string",
         "Parents": [ 
            { 
               "Name": "string"
            }
         ]
      }
   ],
   "OrientationCorrection": "string"
}

構文説明

リクエストは以下のデータを JSON 形式で受け入れます。	
	
[Image]	
	
	base64でエンコードされたバイトまたはS3オブジェクトとしての入力画像。 
	AWSCLIを使用してAmazonRekognitionオペレーションを呼び出す場合、画像バイトの受け渡しはサポートされていません。
	S3バケットに保存されている画像はbase64でエンコードする必要はありません。
	AWSSDKから呼び出す場合は、Bytesフィールドを使用して渡された画像バイトをbase64エンコードする必要がない場合があります。
	タイプ: Image オブジェクト
	指定は必須
	
[MaxLabels]	
	
	レスポンスで返すラベルの最大数。指定された数の最高信頼ラベルを返します。
	タイプ: 整数
	有効範囲: 最小値=0
	指定は必須ではない
	
[MinConfidence]	
	
	返されるラベルの最小信頼度を指定し、指定された値よりも低い信頼度のラベルを返しません。
	もしMinConfidenceが指定されていない場合、55%以上の信頼度値を持つラベルが返されます。
	タイプ: 浮動小数点
	有効範囲: 最小長=0 最大値は 100
	指定は必須ではない

戻り値説明

アクションが成功すると、サービスは HTTP 200 レスポンスを返します。	
サービスから以下のデータが JSON 形式で返されます。	
	
[LabelModelVersion]	
	
	ラベルを検出するために使用されたラベル検出モデルのバージョン番号
	タイプ: 文字列
	
[Labels]	
	
	検出された実世界のオブジェクトのラベルの配列です。
	タイプ: Labelobjectsの配列
	
[方向補正]	
	
	OrientationCorrectionの値は null です
	入力画像が .jpeg 形式の場合、画像の向きを含む交換可能な画像ファイル形式 (Exif) メタデータが含まれている場合があります。
	Amazon Rekognition は、この方向情報を使用して画像補正を行います。
	Exif メタデータの方向情報を使用してイメージの方向を修正すると、バウンディングボックスの座標がオブジェクトの位置を表すように変換されます。
	.png 形式のイメージには Exif メタデータがありません。
	Amazon Rekognition は、画像 Exif メタデータに向き情報がない.png 形式の画像および.jpeg 画像の画像補正を実行しません。
	バウンディングボックスの座標は変換されず、イメージが回転される前のオブジェクトの位置を表します。
	タイプ: 文字列
	有効な値: ROTATE_0 | ROTATE_90 | ROTATE_180 | ROTATE_270

エラー内容

AccessDeniedException	
	
	アクションを実行する権限がありません。
	HTTP ステータスコード:HTTP 400
	
ImageTooLargeException	
	
	入力イメージサイズが制限文字数を超えています。あなたが呼び出す場合DetectProtectiveEquipmentの場合、
	イメージサイズまたは解像度が制限文字数を超えています。
	HTTP ステータスコード:HTTP 400
	
InternalServerError	
	
	Amazon Rekognition でサービスの問題が発生しました。もう一度やり直してください。
	HTTP ステータスコード:HTTP 500
	
InvalidImageFormatException	
	
	指定されたイメージ形式はサポートされていません。
	HTTP ステータスコード:HTTP 400
	
InvalidParameterException	
	
	入力パラメータが制約に違反しています。API オペレーションを再度呼び出す前にパラメータを検証します。
	HTTP ステータスコード:HTTP 400
	
InvalidS3ObjectException	
	
	Amazon Rekognition は、リクエストで指定された S3 オブジェクトにアクセスできません。
	HTTP ステータスコード:HTTP 400
	
ProvisionedThroughputExceededException	
	
	お客様のスループット制限を超えたリクエストの数。この上限を引き上げる場合は、Amazon Rekognition までお問い合わせください。
	HTTP ステータスコード:HTTP 400
	
ThrottlingException	
	
	Amazon Rekognition は一時的にリクエストを処理できませんでした。もう一度やり直してください。
	HTTP ステータスコード:HTTP 500

Rekognition  detect_textメソッド

機能

入力イメージ内のテキストを検出し、コンピュータが読み取り可能なテキストに変換します。	
	
入力画像をbase64でエンコードされた画像バイト、またはAmazonS3バケット内の画像への参照として渡します。	
AWSCLIから呼び出す場合は、AmazonS3バケット内の画像への参照として渡す必要があります。	
AWS CLIの場合、イメージバイトの受け渡しはサポートされていません。	
画像は.pngまたは.jpeg形式のファイルである必要があります。	
	
DetectText操作は、TextDetection要素の配列TextDetectionsでテキストを返します。	
各TextDetection要素は、画像で検出された1つの単語または1行のテキストに関する情報を提供します。	
単語は、スペースで区切られていない1つ以上のISO基本ラテン文字です。	
DetectTextは、画像内の最大100語を検出できます。	
	
行は、等間隔の単語の文字列です。行は必ずしも完全な文ではありません。	
たとえば、運転免許証番号は行として検出されます。	
行の後に整列されたテキストがない場合、行は終了します。	
また、単語の長さに比べて単語間に大きなギャップがある場合、行は終了します。	
つまり、単語間のギャップに応じて、同じ方向に整列されたテキスト内の複数の行を検出する場合があります。	
ピリオドは行の終わりを表すものではありません。文が複数行にまたがる場合、DetectText操作は複数行を返します。	
TextDetection要素がテキスト行であるか単語であるかを判別するには、TextDetectionオブジェクトのTypeフィールドを使用します。	
検出されるには、テキストが水平軸の+/- 90度の方向にある必要があります。	

構文

{
   "Filters": { 
      "RegionsOfInterest": [ 
         { 
            "BoundingBox": { 
               "Height": number,
               "Left": number,
               "Top": number,
               "Width": number
            }
         }
      ],
      "WordFilter": { 
         "MinBoundingBoxHeight": number,
         "MinBoundingBoxWidth": number,
         "MinConfidence": number
      }
   },
   "Image": { 
      "Bytes": blob,
      "S3Object": { 
         "Bucket": "string",
         "Name": "string",
         "Version": "string"
      }
   }
}

戻り値

{
   "TextDetections": [ 
      { 
         "Confidence": number,
         "DetectedText": "string",
         "Geometry": { 
            "BoundingBox": { 
               "Height": number,
               "Left": number,
               "Top": number,
               "Width": number
            },
            "Polygon": [ 
               { 
                  "X": number,
                  "Y": number
               }
            ]
         },
         "Id": number,
         "ParentId": number,
         "Type": "string"
      }
   ],
   "TextModelVersion": "string"
}

構文説明

リクエストは以下のデータを JSON 形式で受け入れます。	
	
[Filters]	
	
	テキストを応答に含めるための条件を設定するためのオプションパラメーター
	タイプ: DetectTextFilters オブジェクト
	指定は必須ではない
	
[Image]	
	
	base64でエンコードされたバイトまたはAmazonS3オブジェクトとしての入力画像。
	AWSCLIを使用してAmazonRekognitionオペレーションを呼び出す場合、画像バイトを渡すことはできません。
	AWSSDKから呼び出す場合は、Bytesフィールドを使用して渡された画像バイトをbase64エンコードする必要がない場合があります。
	タイプ: Image オブジェクト
	指定は必須

戻り値説明

アクションが成功すると、サービスは HTTP 200 レスポンスを返します。	
サービスから以下のデータが JSON 形式で返されます。	
	
[TextDetections]	
	
	入力イメージ内で検出されたテキストの配列。
	タイプ: TextDetectionobjectsの配列
	TextModelバージョン
	テキストの検出に使用されるモデルのバージョン。
	タイプ: 文字列

エラー内容

AccessDeniedException	
	
	アクションを実行する権限がありません。
	HTTP ステータスコード:HTTP 400
	
ImageTooLargeException	
	
	入力イメージサイズが制限文字数を超えています。あなたが呼び出す場合DetectProtectiveEquipmentの場合、
	イメージサイズまたは解像度が制限文字数を超えています。
	HTTP ステータスコード:HTTP 400
	
InternalServerError	
	
	Amazon Rekognition でサービスの問題が発生しました。もう一度やり直してください。
	HTTP ステータスコード:HTTP 500
	
InvalidImageFormatException	
	
	指定されたイメージ形式はサポートされていません。
	HTTP ステータスコード:HTTP 400
	
InvalidParameterException	
	
	入力パラメータが制約に違反しています。API オペレーションを再度呼び出す前にパラメータを検証します。
	HTTP ステータスコード:HTTP 400
	
InvalidS3ObjectException	
	
	Amazon Rekognition は、リクエストで指定された S3 オブジェクトにアクセスできません。
	HTTP ステータスコード:HTTP 400
	
ProvisionedThroughputExceededException	
	
	お客様のスループット制限を超えたリクエストの数。この上限を引き上げる場合は、Amazon Rekognition までお問い合わせください。
	HTTP ステータスコード:HTTP 400
	
ThrottlingException	
	
	Amazon Rekognition は一時的にリクエストを処理できませんでした。もう一度やり直してください。
	HTTP ステータスコード:HTTP 500

①画像に映り込んでいるもののラベル付け情報を表示するプログラム  detect_label.py

import boto3
import json
import sys
from PIL import Image

if len(sys.argv) != 2:
    print('python', sys.argv[0], 'imagefile(JPEG or PNG)')
    exit()

rekognition = boto3.client('rekognition')
with open(sys.argv[1], 'rb') as file:
    result = rekognition.detect_labels(Image={'Bytes': file.read()})
    print(json.dumps(result, indent=4))

image_in = Image.open(sys.argv[1])
w, h = image_in.size
for label in result['Labels']:
    if label['Instances']:
        image_out = Image.new('RGB', (w, h), (200, 200, 200))
        for instance in label['Instances']:
            box = instance['BoundingBox']
            left = int(box['Left']*w)
            top = int(box['Top']*h)
            right = left+int(box['Width']*w)
            bottom = top+int(box['Height']*h)
            image_out.paste(
                image_in.crop((left, top, right, bottom)),
                (left, top))
        name = 'info_'+label['Name']+'_'+sys.argv[1]
        print(name)
        image_out.save(name)

先ほど使った私の画像で情報を取ってみました。

PS C:\Users\mikol\rekognition> python detect_label.py miko1.jpg
{
    "Labels": [
        {
            "Name": "Person",                                (人物)
            "Confidence": 98.7583999633789,
            "Instances": [
                {
                    "BoundingBox": {
                        "Width": 0.445485919713974,
                        "Height": 0.8168341517448425,
                        "Left": 0.37370765209198,
                        "Top": 0.1828351765871048
                    },
                    "Confidence": 98.7583999633789
                }
            ],
            "Parents": []
        },
        {
            "Name": "Human",                                 (人間)
            "Confidence": 98.7583999633789,
            "Instances": [],
            "Parents": []
        },
        {
            "Name": "Garden",                                (庭園)
            "Confidence": 98.5953369140625,
            "Instances": [],
            "Parents": [
                {
                    "Name": "Outdoors"                        (屋外)
                }
            ]
        },
        {
            "Name": "Outdoors",                               (屋外)
            "Confidence": 98.5953369140625,
            "Instances": [],
            "Parents": []
        },
        {
            "Name": "Geranium",  (フウロソウ属はフウロソウ科に分類される多年生草本植物の属の名称)
            "Confidence": 95.00460052490234,
            "Instances": [],
            "Parents": [
                {
                    "Name": "Flower"                           (花)
                },
                {
                    "Name": "Plant"                            (プラント)
                }
            ]
        },
        {
            "Name": "Flower",                                  (花)
            "Confidence": 95.00460052490234,
            "Instances": [],
            "Parents": [
                {
                    "Name": "Plant"                            (プラント)
                }
            ]
        },
        {
            "Name": "Plant",                                   (プラント)
            "Confidence": 95.00460052490234,
            "Instances": [],
            "Parents": []
        },
        {
            "Name": "Blossom",                                 (花)
            "Confidence": 95.00460052490234,
            "Instances": [],
            "Parents": [
                {
                    "Name": "Plant"                            (プラント)
                }
            ]
        },
        {
            "Name": "Arbour",                                  (樹木)
            "Confidence": 75.87372589111328,
            "Instances": [],
            "Parents": [
                {
                    "Name": "Garden"                           (庭園)
                },
                {
                    "Name": "Outdoors"                         (屋外)
                }
            ]
        },
        {
            "Name": "Rose",                                    (ばら)
            "Confidence": 57.35022735595703,
            "Instances": [],
            "Parents": [
                {
                    "Name": "Flower"                            (花)
                },
                {
                    "Name": "Plant"                             (プラント)
                }
            ]
        }
    ],
    "LabelModelVersion": "2.0",
    "ResponseMetadata": {
        "RequestId": "e10b01a7-2bb4-42cc-b0b5-999999999999",
        "HTTPStatusCode": 200,
        "HTTPHeaders": {
            "content-type": "application/x-amz-json-1.1",
            "date": "Tue, 06 Jul 2021 08:55:18 GMT",
            "x-amzn-requestid": "e10b01a7-2bb4-42cc-b0b5-999999999999",
            "content-length": "1111",
            "connection": "keep-alive"
        },
        "RetryAttempts": 0
    }
}
info_Person_miko1.jpg

miko1.jpg に映り込んでいる情報を検出し、「info_Person_miko1.jpg」(右側)が出力された。

  • 人物
  • 樹木←庭園←屋外←
  • フウロソウ属←花←プラント
  • ばら←花←プラント

というように、ラベルの親子関係も含めています。

バウンディングボックスには、私の位置情報が格納されているので、それから貼り付けて生成した出力画像には、私が映っていた。

さらに、わが家で飼っている猫の写真を使ってみた。

PS C:\Users\mikol\rekognition> python detect_label.py Suppy.PNG
{
    "Labels": [
        {
            "Name": "Abyssinian",            (アビシニアン:エジプトの猫?)
            "Confidence": 95.4920425415039,
            "Instances": [],
            "Parents": [
                {
                    "Name": "Cat"             (猫)
                },
                {
                    "Name": "Pet"             (ペット)
                },
                {
                    "Name": "Mammal"          (哺乳類)
                },
                {
                    "Name": "Animal"          (動物)
                }
            ]
        },
        {
            "Name": "Cat",
            "Confidence": 95.4920425415039,
            "Instances": [
                {
                    "BoundingBox": {
                        "Width": 0.8664452433586121,
                        "Height": 0.8476324677467346,
                        "Left": 0.056115493178367615,
                        "Top": 0.05795641243457794
                    },
                    "Confidence": 93.21244812011719
                }
            ],
            "Parents": [
                {
                    "Name": "Pet"
                },
                {
                    "Name": "Mammal"
                },
                {
                    "Name": "Animal"
                }
            ]
        },
        {
            "Name": "Pet",
            "Confidence": 95.4920425415039,
            "Instances": [],
            "Parents": [
                {
                    "Name": "Animal"
                }
            ]
        },
        {
            "Name": "Mammal",
            "Confidence": 95.4920425415039,
            "Instances": [],
            "Parents": [
                {
                    "Name": "Animal"
                }
            ]
        },
        {
            "Name": "Animal",
            "Confidence": 95.4920425415039,
            "Instances": [],
            "Parents": []
        }
    ],
    "LabelModelVersion": "2.0",
    "ResponseMetadata": {
        "RequestId": "06200219-94c8-45d1-98f4-999999999999",
        "HTTPStatusCode": 200,
        "HTTPHeaders": {
            "content-type": "application/x-amz-json-1.1",
            "date": "Tue, 06 Jul 2021 10:07:00 GMT",
            "x-amzn-requestid": "06200219-94c8-45d1-98f4-999999999999",
            "content-length": "722",
            "connection": "keep-alive"
        },
        "RetryAttempts": 0
    }
}
info_Cat_Suppy.PNG
  • アビシニアン:エジプトの猫?←猫←ペット←哺乳類←動物

アメショなんですけど、エジプト猫と思われたみたい。
ラベルが付いて、親子関係も付加されています。

②画像からテキストを抽出するプログラム  detect_text.py

import boto3
import json
import sys
from PIL import Image

if len(sys.argv) != 2:
    print('python', sys.argv[0], 'imagefile(JPEG or PNG)')
    exit()

rekognition = boto3.client('rekognition')
with open(sys.argv[1], 'rb') as file:
    result = rekognition.detect_text(Image={'Bytes': file.read()})
    print(json.dumps(result, indent=4))

image_in = Image.open(sys.argv[1])
w, h = image_in.size
for text in result['TextDetections']:
    image_out = Image.new('RGB', (w, h), (200, 200, 200))
    box = text['Geometry']['BoundingBox']
    left = int(box['Left']*w)
    top = int(box['Top']*h)
    right = left+int(box['Width']*w)
    bottom = top+int(box['Height']*h)
    image_out.paste(
        image_in.crop((left, top, right, bottom)), (left, top))
    name = text['Type']+'_'+text['DetectedText']+'_'+sys.argv[1]
    print(name)
    image_out.save(name)

最近のドラマの宣伝を持ってきて、detect_textメソッドにぶち込んでみた。

PS C:\Users\mikol\rekognition> python detect_text.py Text.PNG
{
    "TextDetections": [
        {
            "DetectedText": "Night Doctor",
            "Type": "LINE",
            "Id": 0,
            "Confidence": 100.0,
            "Geometry": {
                "BoundingBox": {
                    "Width": 0.7902964353561401,
                    "Height": 0.31400784850120544,
                    "Left": 0.11341860145330429,
                    "Top": 0.6729332208633423
                },
                "Polygon": [
                    {
                        "X": 0.11341860145330429,
                        "Y": 0.7004167437553406
                    },
                    {
                        "X": 0.9006854295730591,
                        "Y": 0.6729332208633423
                    },
                    {
                        "X": 0.9037150144577026,
                        "Y": 0.959457516670227
                    },
                    {
                        "X": 0.11644820868968964,
                        "Y": 0.9869410395622253
                    }
                ]
            }
        },
        {
            "DetectedText": "Night",
            "Type": "WORD",
            "Id": 1,
            "ParentId": 0,
            "Confidence": 100.0,
            "Geometry": {
                "BoundingBox": {
                    "Width": 0.3489583432674408,
                    "Height": 0.2744479477405548,
                    "Left": 0.1163194477558136,
                    "Top": 0.7003154754638672
                },
                "Polygon": [
                    {
                        "X": 0.1163194477558136,
                        "Y": 0.7003154754638672
                    },
                    {
                        "X": 0.4652777910232544,
                        "Y": 0.7003154754638672
                    },
                    {
                        "X": 0.4652777910232544,
                        "Y": 0.9747633934020996
                    },
                    {
                        "X": 0.1163194477558136,
                        "Y": 0.9747633934020996
                    }
                ]
            }
        },
        {
            "DetectedText": "Doctor",
            "Type": "WORD",
            "Id": 2,
            "ParentId": 0,
            "Confidence": 100.0,
            "Geometry": {
                "BoundingBox": {
                    "Width": 0.4166666567325592,
                    "Height": 0.21135646104812622,
                    "Left": 0.484375,
                    "Top": 0.7066246271133423
                },
                "Polygon": [
                    {
                        "X": 0.484375,
                        "Y": 0.7066246271133423
                    },
                    {
                        "X": 0.9010416865348816,
                        "Y": 0.7066246271133423
                    },
                    {
                        "X": 0.9010416865348816,
                        "Y": 0.9179810881614685
                    },
                    {
                        "X": 0.484375,
                        "Y": 0.921135663986206
                    }
                ]
            }
        }
    ],
    "TextModelVersion": "3.0",
    "ResponseMetadata": {
        "RequestId": "9a70dd2e-cea8-416e-b254-999999999999",
        "HTTPStatusCode": 200,
        "HTTPHeaders": {
            "content-type": "application/x-amz-json-1.1",
            "date": "Tue, 06 Jul 2021 10:25:21 GMT",
            "x-amzn-requestid": "9a70dd2e-cea8-416e-b254-999999999999",
            "content-length": "1264",
            "connection": "keep-alive"
        },
        "RetryAttempts": 0
    }
}
LINE_Night Doctor_Text.PNG
WORD_Night_Text.PNG
WORD_Doctor_Text.PNG

LINE_Night Doctor_Text.PNG
WORD_Night_Text.PNG
WORD_Doctor_Text.PNG

の3つの画像データが、detect_textメソッドから出力された。
以下に、元の画像データと、出力された3つの画像データを並べた。

テキストを切り出そうとした元の「Text.PNG」

LINE_Night Doctor_Text.PNG(行を抜き出し)


WORD_Doctor_Text.PNG(単語行を抜き出し)

WORD_Night_Text.PNG(単語行を抜き出し)

テキストを切り出そうとした元の「Text.PNG」から順番に、その下に並べた。

LINE_Night Doctor_Text.PNG
WORD_Doctor_Text.PNG
WORD_Night_Text.PNG

LINEが頭に付くファイルは、単語を抜き出したファイルです。
WORDが頭に付くファイルは、単語を抜き出したファイルです。

text_detectメソッドは日本語にまだ対応していないので、処理できる画像を選んで実行しました。