LoginSignup
0
0

More than 1 year has passed since last update.

#8 PowerApps アプリ で撮影した画像を FaceAPI で感情分析してみました(累積分析)

Last updated at Posted at 2021-05-19

概要

PowerApps のカメラで撮影した画像を API Management 経由で FaceAPI に転送し、その画像の分析結果「感情、性別、年齢」を PoweApps に表示するアプリの実装手順を数回に分け記載しています。また、並行して、その分析結果を CosmosDB に保存しておき、PowerApps からの累積情報取得リクエストにより累積クエリ結果「感情分布、性別分布」を PowerApps に返し、円グラフ表示する機能の実装手順も複数回に分け記載します。なお、PowerApp の画面作成については省略し、APIコール部分とその戻り値の部分に焦点をあてて記載しています。
本アプリの全体構成は下図となります。
image.png

今回(第8回目)の構成箇所は下図の部分となります。
image.png

第8回目は、第7回 で作成した CosmosDBへのデータ登録ローカルプログラムを 第2回 で作成した Azure Functions に追加します。


実行環境

macOS Big Sur 11.3
Python 3.8.3


Azure Portal 上での CosmosDB のキーとエンドポイント情報(クレデンシャル情報)の定義

前回取得した CosmosDB のキーとエンドポイント情報(「COSDB_KEY」「COSDB_ENDPOINT」)を 第2回で作成した Azure Functions「iturufuncface」の「設定」 ー 「構成」 ー 「+新しいアプリケーション設定」 で追加定義しておきます。あわせて、CosmosDB Container情報(「COSDB_DATABASE」「COSDB_CONTAINER」)も追加定義しておきます。
image.png


Functions で実行されるコード

第2回で作成したコードプログラムを以下のように変更します(CosmosDBへのデータ登録部分の追加)。

__init__.py
import logging
import azure.functions as func
import cognitive_face as CF
import numpy as np
import os
import json
import base64
import requests
from datetime import datetime
from azure.cosmos import exceptions, CosmosClient, PartitionKey

# FaceAPIの情報
KEY = os.environ['FACE_KEY']
ENDPOINT = os.environ['FACE_ENDPOINT']
ENDPOINTDETECT = os.environ['FACE_ENDPOINTDETECT']

# emotion定義
emobase = ['anger', 'contempt', 'disgust', 'fear', 'happiness', 'neutral', 'sadness', 'surprise']
emolist = ['怒', '侮', '嫌', '恐', '幸', '無', '悲', '驚']

# Azure CosmosDB Info
COSDB_KEY = os.environ['COSDB_KEY']
COSDB_ENDPOINT = os.environ['COSDB_ENDPOINT']
DatabaseName = os.environ['COSDB_DATABASE']
ContainerName = os.environ['COSDB_CONTAINER']


# 感情分析(Base64-Binary)
def face_emotion_base64(data64, name) :
    logging.info("face_emotion_base64 by iTuru")
    # data = base64.b64decode(data64.replace("data:image/png;base64,", ""))
    data = base64.b64decode(data64[22:])

    try:
        headers = {
            'Content-Type': 'application/octet-stream',
            'Ocp-Apim-Subscription-Key': KEY,
        }

        params = {
            'returnFaceId': 'false',
            'returnFaceLandmarks': 'false',
            'faceRectangle': 'false',
            'returnFaceAttributes': 'age,gender,emotion',
        }

        # FACE_APIでの感情分析取得
        response = requests.post(ENDPOINTDETECT, params=params, headers=headers, data=data)
        j=json.loads(response.text)

        # 分析する対象の画像に複数人いようが最初の1人のみを分析の対象とする
        # 年齢情報
        age_data = j[0]['faceAttributes']['age']
        logging.info(age_data)

        # 感情情報
        emotion_data = j[0]['faceAttributes']['emotion']
        logging.info(emotion_data)

        # 性別情報
        gender_data = j[0]['faceAttributes']['gender']
        logging.info(gender_data)

        # 感情分析結果からスコアのみを分別
        emotion = []
        for name in emobase:
            emotion.append(emotion_data[name])

        # スコアの高いものをその人の感情として決定する 
        # logging.info(emotion)
        num = np.argmax(emotion)
        emotion_weight = str(emotion[num]*100) + "%,  " + gender_data + ",  " + str(age_data)
        emotion_dict = {'emotion': emolist[num], 'data': emotion_weight}    # 辞書データの作成
        emotion_json = json.dumps(emotion_dict, ensure_ascii=False)         # Jsonエンコード

        # CosmosDBへの取得した分析情報の追加
        cosmosdb_create_item(age_data, gender_data, emolist[num], emotion[num])

        return emotion_json

    except Exception as e:
        logging.info(e)
        emotion_dict = {'emotion': "ERROR !!!", 'data': "--.-%"}            # 辞書データの作成
        emotion_json = json.dumps(emotion_dict, ensure_ascii=False)         # Jsonエンコード
        return emotion_json


# CosmosDBへのアイテム追加
def cosmosdb_create_item(age_data, gender_data, emotion_emo, emotion_data) :

    # CosmosDBへの接続
    client = CosmosClient(COSDB_ENDPOINT, COSDB_KEY)
    db = client.get_database_client(DatabaseName)
    container = db.get_container_client(ContainerName)

    # 現時刻取得しIDとして使用するために文字列に変換
    now_ts_str = str(datetime.now().timestamp())

    # 新規登録項目の作成
    newItem = {
        "id": now_ts_str,
        "age": age_data,
        "gender": gender_data,
        "emotion": emotion_emo,
        "data": emotion_data
    }

    # アイテムの追加
    item = container.create_item(newItem)
    logging.info(' --- create_item --- ')
    logging.info(item)


# main関数
def main(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')

    name = req.params.get('name')
    req_body = req.get_body()
    res_emotion = face_emotion_base64(req_body, name)

    return func.HttpResponse(
            res_emotion,
            mimetype = "application/json",
            status_code = 201
    )

ローカルでコードを実行してみます

コードを実行する前に、ローカルで環境情報を取得します。以下のコマンドを実行します。

$ func azure functionapp fetch-app-settings iturufuncface
App Settings:
Loading APPINSIGHTS_INSTRUMENTATIONKEY = *****
Loading APPLICATIONINSIGHTS_CONNECTION_STRING = *****
Loading AzureWebJobsStorage = *****
Loading COSDB_CONTAINER = *****
Loading COSDB_DATABASE = *****
Loading COSDB_ENDPOINT = *****
Loading COSDB_KEY = *****
Loading FACE_ENDPOINTDETECT = *****
Loading FACE_KEY = *****
Loading FUNCTIONS_EXTENSION_VERSION = *****
Loading FUNCTIONS_WORKER_RUNTIME = *****

その後、コードを実行します。

$ func host start                                   
Found Python version 3.8.3 (python3).

Azure Functions Core Tools
Core Tools Version:       3.0.3442 Commit hash: 6bfab24b2743f8421475d996402c398d2fe4a9e0  (64-bit)
Function Runtime Version: 3.0.15417.0

Functions:
    HttpTrigger: [GET,POST] http://localhost:7071/api/HttpTrigger

For detailed output, run func with --verbose flag.
[2021-05-16T05:50:27.421Z] Worker process started and initialized.
[2021-05-16T05:50:32.490Z] Host lock lease acquired by instance ID '0000000000000000000000002CF30641'.

Functions へのアクセスプログラムの実行

結果確認のためのFunctionsへのアクセスプログラムは以下となります(第2回のものと同じ)。

PostImage.py
import requests
import base64

url = "http://localhost:7071/api/HttpTrigger"
headers = {
    'Content-Type': 'application/octet-stream',
}

with open("001.png", "rb") as f:
    data = "data:image/png;base64," + base64.b64encode(f.read()).decode("UTF-8")

try:
    response = requests.post(url, headers=headers, data=data)
    print(response.text)
except Exception as e:
    print(e)

実行結果は以下となります(画像はローカルにあるものを使用)。

$ python PostImage.py
{"emotion": "幸", "data": "98.2%,  female,  18.0"}

ローカルFunctions側の実行結果は以下となります。

[2021-05-16T05:52:45.269Z]  --- create_item --- 
[2021-05-16T05:52:45.269Z] {'id': '1621403564.974882', 'age': 18.0, 'gender': 'female', 'emotion': '幸', 'data': 0.982, '_rid': '-MxGAMsRyx4vAAAAAAAAAA==', '_self': 'dbs/-MxGAA==/colls/-MxGAMsRyx4=/docs/-MxGAMsRyx4vAAAAAAAAAA==/', '_etag': '"e801233a-0000-2400-0000-60a4a7ad0000"', '_attachments': 'attachments/', '_ts': 1621403565}
[2021-05-19T05:52:45.527Z] Executed 'Functions.HttpTrigger' (Succeeded, Id=29c35e25-9865-4978-8bcc-4831711fe9cd, Duration=1865ms)

問題なくデータ登録できました。これで Functions からFaceAPI をコールし、その分析結果を CosmosDB に登録できることを確認できました。


Azureへのデプロイ

ローカルで動作確認したコードをAzureにデプロイします。

$ func azure functionapp publish iturufuncface           
Getting site publishing info...
Creating archive for current directory...
Performing remote build for functions project.
    :
    中略
    :
Deployment successful.
Remote build succeeded!
Syncing triggers...
Functions in iturufuncface:
    HttpTrigger - [httpTrigger]
        :

正常にデプロイできました。


次回について

次回(第9回)は、第7回 で作成した CosmosDBからのデータ取得ローカルプログラムを、新たに作成する Azure Functions に実装させ、感情毎の件数と性別毎の件数を CosmosDB からArray型のJSON形式で取得できることを確認します。


参考情報

以下の情報を参考にさせていただきました。感謝申し上げます。
Azure Cosmos DB - 基本操作(Python)
PythonからAzure Comos DBのデータ操作
Azure Functions App × Python で Azure CosmosDB にアクセスしてみる
Azure cosmosDB のアラート設定について

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0