【AWS】Amazon Rekognitionを用いた車のナンバープレートの認識

Amazon Rekognitionは、画像やビデオの分析AWSサービスです。

Amazon Rekognitionの主な機能と用途

https://docs.aws.amazon.com/rekognition/latest/dg/what-is.html

1. 画像分析:
Rekognitionは、アノテーション、物体の検出、シーンの認識、顔検出と識別、顔の属性分析（性別、年齢など）、顔の比較、テキストの検出など、さまざまな画像分析機能を提供します。

1. ビデオ分析:
Rekognitionは、ビデオ内のフレームごとに顔検出、物体検出、シーン認識、人物のトラッキングなどのビデオ分析を実行できます。リアルタイムビデオストリームの処理にも対応しています。

1. 顔認識:
Rekognitionは、画像やビデオ内の顔を検出・認識し、顔の比較や一致度のスコアリングを行うことができます。これにより、顔認識ベースのアプリケーションやサービスの構築が可能となります。

1. ラベル検出:
Rekognitionは、画像やビデオ内のオブジェクトやシーンに関連するラベルを検出します。例えば、人、車、犬、山など、さまざまなラベルを自動的に識別することができます。

1. テキスト検出:
Rekognitionは、画像内のテキストを検出し、文字列として抽出することができます。この機能を使用して、書かれたテキストを自動的に抽出して分析することができます。

認識可能なラベルはAmazon Rekognitionコンソール画面から、「全リストをダウンロードする」から取得可能です。

Amazon RekognitionのPythonライブラリ一覧

https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/rekognition.html

## 使い方
```Python
import os
import sys
import re
import datetime
import logging
from pathlib import Path
from PIL import Image
import io
import boto3
from botocore.exceptions import ClientError

dt_now = datetime.datetime.now()
str_dt_now = dt_now.strftime('%Y-%m-%d-%H-%M-%S')

logger = logging.getLogger(__name__)

cropimg_path = Path("crop_img")
if not cropimg_path.exists():
    cropimg_path.mkdir(parents=True)

AWS_ACCESS_KEY_ID=***************
AWS_SECRET_ACCESS_KEY=********************************
AWS_SESSION_TOKEN=********************************************************
AWS_REGION='us-east-1'

rekognition_client = boto3.client('rekognition', 
                        aws_access_key_id=AWS_ACCESS_KEY_ID, 
                        aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
                        region_name=AWS_REGION,
                        aws_session_token= AWS_SESSION_TOKEN)

documentName = r"sample.jpg"

# 画像をByteデータに
with open(documentName, 'rb') as document:
    imageBytes = bytearray(document.read())

# Amazon Rekognitionで物体検知
response = rekognition_client.detect_labels(Image={"Bytes": imageBytes}, MaxLabels=1000, MinConfidence=0)

plate_detected = False
license_plate_list = []
for idx,item in enumerate(response['Labels']):
    if item["Name"] == "License Plate":
        plate_detected = True
        print('\n############ ナンバープレートを検知しました ##############')
        print(idx, item['Name'], item['Confidence'], item['Categories'][0]['Name']) #item['Parents']
        if len(item['Instances']) != 0:
            for i in item['Instances']:
                license_plate_list.append([i['BoundingBox']['Width'],i['BoundingBox']['Height'],i['BoundingBox']['Left'],i['BoundingBox']['Top']])

print('ナンバープレートの検知数 : ', len(license_plate_list))

# 検知した部分ごとに、OCRして、ナンバープレートかどうか判定
for idx,item in enumerate(license_plate_list):
    license_plate_loc = item
    # OCR for number plate on filtered image location.
    PlateNumber = rekognition_client.detect_text(Image={"Bytes": imageBytes},
                                                 Filters={
                                                     'RegionsOfInterest': [
                                                         {
                                                            'BoundingBox': {
                                                                'Width': license_plate_loc[0],
                                                                'Height': license_plate_loc[1],
                                                                'Left': license_plate_loc[2],
                                                                'Top': license_plate_loc[3]
                                                            },
                                                        },
                                                    ]
                                                })
    # get license number by Regex from OCR text data.
    classification_number, plate_number = '', ''
    for elem in PlateNumber['TextDetections']:
        #confidence_score = elem['Confidence']
        elem = elem['DetectedText']
        # 3桁の分類番号
        matches = re.findall(r"\d{3}", elem)
        if matches != []:
            classification_number = matches[0]
        # ハイフンで区切られた4桁のナンバー
        matches = re.findall(r"\d{2}-\d{2}", elem)
        if matches != []:
            plate_number = matches[0]

    if classification_number.isnumeric() and plate_number != '':
        print(idx, '分類番号: ', classification_number, ' ナンバー: ', plate_number)
        # save extract image for number plate.
        image = Image.open(io.BytesIO(imageBytes))
        Maxwidth, Maxheight = image.size
        x1 = license_plate_loc[2] * Maxwidth
        y1 = license_plate_loc[3] * Maxheight
        x2 = (license_plate_loc[2] + license_plate_loc[0]) * Maxwidth
        y2 = (license_plate_loc[3] + license_plate_loc[1]) * Maxheight
        cropped_image = image.crop((x1, y1, x2, y2))
        documentName = cropimg_path / Path('cropped_image_%s_%s.jpg'%(str_dt_now, idx))
        cropped_image.save(documentName)
```
元画像(smaple.jpg)
![22fa40424d2803492de748eb990d8074.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483556/112bf452-ccbc-178d-6bfe-e613aa7d97a3.jpeg)

ナンバーを検知して、検知部分のみを切り取った画像
![cropped_image_2023-06-02-15-52-22_0.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483556/a139cdf0-6b07-539b-4d9b-b9429b58677c.jpeg)

OCRの結果
```
分類番号:  400  ナンバー:  20-79
```

## 参考文献

https://github.com/dyama/alpr_jp