#はじめに
中の人はエンジニアのeの字も知らない初学者です。文章やコードに不備があるかもしれませんのであたたかい目でみていただければと思います。。
また,理解が不足している部分があればぜひコメントで教えて頂きたいです。
#作成の経緯と参考サイト
AIについて勉強していた折に,卒業アルバム作りにAIを活用したという記事を見まして。
DXとしてもすごくアツい話題だと思ったので,最終目標を「擬似卒アル作成アプリ」として勉強を始めました。
FaceAPIというものを使うと顔認識が簡単にできるとのことなので,まずは初めの段階としてFaceAPIを使って顔を認識するアプリを作成してみました。
(FaceAPIにたどり着くまでにも紆余曲折を経たのですが,それについては別の記事を書ければと考えています。)
以下のサイトに大変お世話になりました。
【Microsoft Azure Face】Pythonで画像の中の顔を認識してみる(SDK)
#1.Azure FaceAPIのダウンロード
Microsoft Azureの公式サイト
クイック スタート:Face クライアント ライブラリを使用する
に従い,①Azureのアカウントを作成②Faceリソースの作成③キーとエンドポイントの取得
#2.実行するコードを記述
import streamlit as st
from azure.cognitiveservices.vision.face import FaceClient
from msrest.authentication import CognitiveServicesCredentials
from PIL import Image, ImageDraw, ImageFont
import os
# タイトルを設定
st.title("顔検出アプリ")
# ローカル環境変数よりキーとエンドポイントを取得
KEY = os.environ.get('KEY')
ENDPOINT = "https://エンドポイントのURL/"
# サブスクリプションキー情報を使用してインスタンス化
face_client = FaceClient(ENDPOINT, CognitiveServicesCredentials(str(KEY)))
# Streamlitでアップローダーを作成
uploded_file = st.file_uploader("jpg画像をアップロードしてください。顔を検出します。", type="jpg")
if uploded_file is not None:
# 画像をtmb.jpgとして一時保存
img = Image.open(uploded_file)
img.save("tmb.jpg")
image_data = open("tmb.jpg", 'rb') # 画像をバイナリーデータに変換
# 顔の検出
detected_faces = face_client.face.detect_with_stream(
image_data,
return_face_landmarks=True,
return_face_attributes=['accessories','age','emotion','gender','glasses','hair','makeup','smile'])
if not detected_faces:
raise Exception('画像から顔を検出できませんでした。')
# 認識された顔周辺に四角を描く関数
def getRectangle(faceDictionary):
rect = faceDictionary.face_rectangle
left = rect.left
top = rect.top
right = left + rect.width
bottom = top + rect.height
return ((left, top), (right, bottom))
# 認識された顔の上に年齢を描く関数
def getAge(faceDictionary):
rect = faceDictionary.face_rectangle
left = rect.left
top = rect.top - 30
return (left, top)
# イメージオブジェクト生成
image_data = Image.open("tmb.jpg")
drawing = ImageDraw.Draw(image_data)
# 関数を呼び出して、顔に四角を描く
for face in detected_faces:
drawing.rectangle(getRectangle(face), outline='Red', width = 3)
drawing.text(getAge(face), str(face.face_attributes.age), font = ImageFont.truetype("arial.ttf", size=30), align = 'Left', fill = 'Red')
st.image(image_data, caption='Uploaded Image', use_column_width=True)
#3.Herokuでのデプロイ
###①必要なファイルを作成
必要なファイルは以下の6種類です。
・main.py
・requirements.txt
streamlit
Pillow
msrest
azure-cognitiveservices-vision-face
・setup.sh
mkdir -p ~/.streamlit/
echo "\
[general]\n\
email = \"メールアドレス\"\n\
" > ~/.streamlit/credentials.toml
echo "\
[server]\n\
headless = true\n\
enableCORS=false\n\
port = $PORT\n\
" > ~/.streamlit/config.toml
・Procfile
web: sh setup.sh && streamlit run main.py
・フォントファイルのコピーたち(下記で説明しています。)
###②Gitでデプロイ
作業ディレクトリに移動します。
$ cd "作業ディレクトリへのパス"
Herokuにログイン
$ heroku login
create new appから新しくアプリを立ち上げます。
$ heroku git:remote -a "アプリ名"
サブスクリプションキーを環境変数に設定します。
$ heroku config:set KEY=xxx
Gitの初期化とcommitをします。
$ git init
$ git add.
$ git commit -m "first commit"
インスタンスを作成します。
$ heroku create
pushして開きます。
$ git push heroku master
$ heroku open
アプリが正常に作動すれば完了です。
躓いた点と解決法
###①サブスクリプションキーを公開しない工夫→環境変数の設定
セキュリティ対策のため,初めはseacret.json
を通してキーを得ようとしたのですが,うまくいきませんでした。
Herokuにデプロイする際に,ターミナルで以下を実行することで,ローカルに環境変数を設定することができました。
heroku config:set KEY="xxx"
恐らくWeb開発をしている方にとっては常識なんだろうなー…でもこの段階でも躓きました。
###②画像をバイナリーデータに変換できない→一度名前を付けて保存
FaceAPIに渡す画像データはバイナリデータでないといけないということで,ここもかなり苦労しました。当初は以下のコードでBytesIO
を用いてバイナリデータを返そうとしました。
img = Image.open(uploded_file)
with io.BytesIO() as output:
img.save(output, 'jpeg')
image_data = output.getvalue()
が,以下のエラー
AttributeError: 'bytes' object has no attribute 'read'
どうやらbytes
オブジェクトはread
に対応していませんよーといったことのようです。アップロードされた画像を,一度名前を付けてopen
を使用して保存することによって解決できました。
# 画像をtmb.jpgとして一時保存
img = Image.open(uploded_file)
img.save("tmb.jpg")
image_data = open("tmb.jpg", 'rb') # 画像をバイナリーデータに変換
###③drawing.textのフォント指定がうまくいかない→Fontファイルもアップロード
Streamlitはターミナルでstreamlit run "ファイル名".py
と実行することによってアプリのデモを見ることができます。デモ上でアプリが問題なく作動したのを確認して,いざデプロイ!完了して,動作確認のため画像をアップロードしたところ,エラーが。。。
エラーメッセージは特になかったと記憶しています。しかし,以下のコードで対応するfont
が見つからないとのだろうなということは何となくわかりました。
# 関数を呼び出して、顔に四角を描く
for face in detected_faces:
drawing.rectangle(getRectangle(face), outline='Red', width = 3)
drawing.text(getAge(face), str(face.face_attributes.age), font = ImageFont.truetype("arial.ttf", size=30), align = 'Left', fill = 'Red')
解決策として,Fontsファイルにあった該当の文字フォントファイルを,作業ディレクトリに丸ごとコピーしました。フォントファイルも一緒にデプロイすることで,なんとか解決できました。どこかで調べたわけではないので,正攻法ではないと思いますが…
###④Streamlitへのデプロイで公開されない→Herokuでデプロイ
Streamlitにてアプリをデプロイ後,念のため…と思って他のアカウントから開いてみたところ
You do not have access to this app or it does not exist
とエラー。
恐らくGithubのレポジトリを非公開にしていることが原因かと思います。Githubのレポジトリを非公開にしているのは,.gitignore
ファイルをうまく扱えずseacret.json
が丸出しになってしまったためです。
以下のサイトを参考にしてHerokuでアプリをデプロイすることで解決しました。
【簡単爆速第2弾】Streamlitをherokuにデプロイ
#まとめ
FaceAPIを用いてWebアプリを作ることができました。
正直ここに辿り着くまでに多くのエラーと戦い,かなり苦労しました。無事にアプリが作動した時にはそれはそれは嬉しかったです。叫びました笑
そして今回はStreamlitの力を借りてHTMLやCSSを使わずの実装だったので,まだまだ勉強の余地はありますね。
おわり