雑コラBOTを作ろう⑦: Azure Functionsで画像加工をして雑コラを作る

貼り付ける顔画像を取得するためのバインドを設定します。

画像処理用の関数の「統合」メニューで「+新しい入力」をクリックし、「Azure BLOB ストレージ」バインドを選択します。

「Azure BLOB ストレージ input」のパスには、先ほどの画像を保存したBlob Storageのパスを指定し、保存をクリックします。

ライブラリのインストール

PythonライブラリをAzure Functionsにインストールします。

今回は、Pythonの画像処理ライブラリ「Pillow」を使ってコラ画像を作成するため、KuduのCMDコンソールで下記のコマンドを入力し、「Pillow」をインストールします。

※ ライブラリのインストールに関してはこちらの記事 も参照ください。

D:\home\site\tools\python.exe -m pip install Pillow

スクリプト

スクリプトは以下のように作成しました。

coding: utf-8

1. ライブラリの読み込み

import os, json
import imghdr
import urllib.parse
import urllib.request
import hashlib

import io
from PIL import Image

バインド設定の読み込み

with open(os.path.dirname(_file_) + "/function.json", "r") as f:
bindings = json.load(f)
bindings = {bind["name"]: bind for bind in bindings["bindings"]}

Face APIキー情報

faceapikey = "{API Key}"
faceapiendpoint = "https://japaneast.api.cognitive.microsoft.com/face/v1.0"

Face APIへのリクエスト

def detectfacefromdata(apikey, body, endpointorlocation="japaneast", returnFaceId=True, returnFaceLandmarks=False, returnFaceAttributes=[]):

URL生成

params = urllib.parse.urlencode({
"returnFaceId": returnFaceId,
"returnFaceLandmarks": returnFaceLandmarks,
})
if returnFaceAttributes != []:
params["returnFaceAttributes"] = ",".join(returnFaceAttributes)

if "http" in endpointorlocation:
url = "{}/detect?{}".format(endpointorlocation, params)
else:

url = "https://{}.api.cognitive.microsoft.com/face/v1.0/detect?{}".format(endpointorlocation, params)

リクエストヘッダ

headers = {
"Ocp-Apim-Subscription-Key": api_key,
"Content-Type": "application/octet-stream"
}

HTTPリクエスト

req = urllib.request.Request(url, data=body, headers=headers)
try:
with urllib.request.urlopen(req) as res:
res_json = json.loads(res.read())

except urllib.error.HTTPError as err:
print(err.code)
print(err.read())
res_json = []

except urllib.error.URLError as err:
print(err.reason)
res_json = []

return res_json

print("Function Start. ID:" + os.environ["INVOCATIONID"])

def handler():

メッセージ読み込み

with open(os.environ["inputMessage"], "r") as f:
input_msg = json.load(f)

コンテンツのダウンロード

print("Downloading input image")
dlurl = inputmsg["img_url"]
req = urllib.request.Request(dl_url)
with urllib.request.urlopen(req) as res:
img_bytes = res.read()

実行履歴の取得

outputpath = bindings["outputBlob"]["path"].format(blobpath=inputmsg["blobpath"]).split("/")
container = output_path[0]
blobpath = "/".join(outputpath[1:])
imgname = outputpath[-1]
imgmd5 = hashlib.md5(imgbytes).hexdigest()

画像フォーマット判定

print("Checking input image format")
imgformat = imghdr.what(None, h=imgbytes)

if img_format in ["jpeg", "png", "bmp", "gif"]:

顔を検出

print("Detecting Faces")

detectedfaces = detectfacefromdata(faceapikey, imgbytes, endpointorlocation=faceapi_endpoint)

if len(detected_faces):
resultmessage = "%s face(s) datected." % len(detectedfaces)

else:
result_message = "No faces detected."

print(result_message)

2. 画像データをPillowの形式に変換

imgpil = Image.open(io.BytesIO(imgbytes))
imgrawface = Image.open(os.environ["inputBlob"])

for faceinfo in detectedfaces:

3. 検出した顔の位置・サイズ情報を取得

facerect = faceinfo["faceRectangle"]

4. 顔画像のリサイズ

pasteface = imgraw_face.copy()
pasteface.thumbnail((facerect["width"], face_rect["height"]), Image.ANTIALIAS)

5. 顔画像の貼り付け

imgpil.paste(pasteface, (facerect["left"], facerect["top"]))

6. 画像の出力

imgpil.save(os.environ["outputBlob"], imgformat.upper(), quality=100)
#with open(os.environ["outputBlob"], "wb") as fw: # 置き換え
#fw.write(img_bytes)

出力メッセージの設定

result = {
"status": "ok",
#"message": "Image is uplaoded.",
"message": result_message,
"container": container,
"blobpath": blobpath,
"filename": img_name,
"contentmd5": imgmd5,
"input": input_msg
}

else:

出力メッセージの設定

result = {
"status": "error",
"filename": img_name,
"contentmd5": imgmd5,
"input": input_msg,
}

if img_format is None:
result["message"] = "Input file is not image."
else:
result["message"] = "Unsupported image format."

キューメッセージの出力

print("Output Queue Message")
with open(os.environ["outputQueueItem"], "w") as fw:
json.dump(result, fw)

実行履歴の書き込み

print("Insert Log to Table")
insert_entity = {key: val for key, val in result.items()}
insert_entity.update({
"PartitionKey": "results",
"RowKey": os.environ["INVOCATIONID"]
})

with open(os.environ["outputTable"], "w") as fw:
json.dump(insert_entity, fw)

return

if _name == "main_":
handler()

スクリプトの説明

1. ライブラリの読み込み

必要なライブラリを読み込みます。
画像処理用の「Pillow」とBytes型で保持している画像データをPillowで読み込めるように「io」を追加しています。

2. 画像データをPillowの形式に変換

Bytes型の画像データをPillowで読み込みます。
ここで、顔画像も同時に読み込んでおきます。

3. 検出した顔の位置・サイズ情報を取得

Face APIで顔を検出した顔のサイズを取得します。
検出結果の「faceRectangle」キーから、顔の左上端の座標と顔の幅・高さのピクセル数が取得できます。

4. 顔画像のリサイズ

貼り付ける顔画像を貼り付け先の顔のサイズにリサイズします。

5. 顔の貼り付け

リサイズした顔画像を貼り付けます。

6. 画像の出力

Pillowで加工した画像データを保存します。
Pillowから画像の保存ができるため、画像出力のスクリプトを置き換えています。

実行結果

Slackのチャンネルに画像のURLを投稿するとコラ画像が返ってきます

同様に画像を5人の子供の写真に変更しても、雑コラされた

同様に画像を5人の子供の写真に変更しても、雑コラされた

まとめ

「雑コラBot」の紹介含め8回にわたった「雑コラBotを作ろう」は、今回の雑コラBot完成でようやく最終回となりました。このBotを使えば、たくさんのコラ画像を効率的に作成でき、業務最適化に役立てられます。

「雑コラ」なんてしないという方も、これまでに紹介した設定はAzure Functionsを使用する上での基本的なものであるため、Azure Functionsを使う場合にはぜひ参考にしてみてください。

ちなみに完成した「雑コラBot」は、記事作成の過程でパフォーマンス最適化を行ったことで、最初に紹介した雑コラBotと比較してレスポンス時間が1/3~1/2程度まで短縮されました。

次回は、Azure Functionsのパフォーマンス最適化について検証していきます。

この記事を書いた人

azure-recipe-user