技術雑記帳兼日記帳

AWS、Python、Terraformの使い方をコッソリ

python Amazon Rekognition detect_facesその2

はじめに

detect_facesの結果を分析してBoundingBoxと顔のパーツの位置を埋め込む処理を実装した。
流石にカート・コバーンの画像を使うのは気が引けるので画像なしで。

準備

下記のプログラムを用意する。

  • detect_faces.py
import boto3, sys, json, cv2, math, os, traceback

#detect_facesで顔の分析結果を返す
def detect_faces(in_image_file):
    client=boto3.client('rekognition')
    #写真を読み出して、Byte変換してdetect_faces送信
    file = open(in_image_file,'rb')
    response = client.detect_faces(Image={'Bytes':file.read()},Attributes=['ALL'])

    return response

#BoundingBoxを設定して返す
def set_bounding_box(image_file, bounding_box_data):
    #画像の高さと幅を取得
    imgHeight, imgWidth = image_file.shape[:2]

    #位置を算出
    left = math.ceil(bounding_box_data['Left'] * imgWidth)
    top = math.ceil(bounding_box_data['Top'] * imgHeight)
    width = math.ceil(bounding_box_data['Width'] * imgWidth)
    height = math.ceil(bounding_box_data['Height'] * imgHeight)

    image_file = cv2.rectangle(image_file
                            , (left, top)
                            , (width+left, height+top)
                            , (0,255,0)
                            , 2)

    return image_file

#Landmarksを設定して返す
def set_landmarks(image_file, landmarks):
    #画像の高さと幅を取得
    imgHeight, imgWidth = image_file.shape[:2]

    eyeLeft = get_lndmark('eyeLeft', landmarks)
    eyeRight = get_lndmark('eyeRight', landmarks)
    mouthLeft = get_lndmark('mouthLeft', landmarks)
    mouthRight = get_lndmark('mouthRight', landmarks)
    nose = get_lndmark('nose', landmarks)

    landmark_taple_list = [eyeLeft, eyeRight, mouthLeft, mouthRight, nose]
    
    #位置を算出
    for landmark_taple in landmark_taple_list:
        X = math.ceil(landmark_taple[0] * imgWidth)
        Y = math.ceil(landmark_taple[1] * imgHeight)

        image_file = cv2.rectangle(image_file
                                , (X, Y)
                                , (X, Y)
                                , (0,255,0)
                                , 10)

    return image_file

#landmarkを指定してX,Y取得
def get_lndmark(landmark_type, landmarks):
    for landmark in landmarks:
        if(landmark['Type'] == landmark_type):
            return (landmark['X'], landmark['Y'])

if __name__ == '__main__':
    if len(sys.argv) != 3:
        exit()
    
    in_file_name = sys.argv[1]
    out_file_name = sys.argv[2]

    try:
        #顔分析
        #jsonが無ければ分析あれば使う
        json_file_name = in_file_name + '.json'
        if (os.path.exists(json_file_name) == True):
            file = open(json_file_name, 'r')
            response = file.read()
            response = json.loads(response)
        else:
            response = detect_faces(in_file_name)
            file = open(json_file_name, 'w')
            file.write(json.dumps(response))

        #分析元の画像を読み込み
        origin_image = cv2.imread(in_file_name)
        after_image = origin_image.copy()
        
        #画像に分析結果の設定
        for faceDetail in response['FaceDetails']:
            #boundingboxを設定
            print('対象の年齢は'+ str(faceDetail['AgeRange']['Low']) + 'から' + str(faceDetail['AgeRange']['High']))
            after_image = set_bounding_box(after_image, faceDetail['BoundingBox'])
            after_image = set_landmarks(after_image, faceDetail['Landmarks'])
            
        
        #結果の出力
        if after_image is not None:
            cv2.imwrite(out_file_name, after_image)


    except Exception as e:
        print(e)
        print(traceback.format_exc())

実行方法

python detect_faces.py 分析したいファイル.jpg 出力したいファイル名.jpg

今回のは関数分けをして見通しを良くしたのと何度も分析しないようにレスポンスを外に吐き出すようにした。
例外も盛り込んでNG処理をスッキリさせることもできた。
landmarkはX,Yの2値なのでタプルを作って、タプルの配列に設定してforで回してみた。


まとめ

大体やってみたいことは実装できた。
タプルの取得と配列設定はもう少しスッキリかっこよく書く方法はないものかな?