顔検出APIをFlask × OpenCVで作ってVue.jsでたたくだけ
最初に勘違いして欲しくないのが、顔を検出しているだけという点です。
顔を認識して、誰かを判別できたりはしません…次回以降で作りたいなとは思っていますが。
今回の構成は以下の通り。
- API:Flask
- クライアント:Vue.js
- 画像処理:OpenCV
めちゃくちゃ単純ですね。
実際に使って見ると以下のような画像が生成されます。
Photo by Omar Lopez on Unsplash
Photo by Omar Lopez on Unsplash
見て頂くと分かるのですが、誤判定も多々あります…
尚、動作検証はWindowsのみでしか行っていません。
また、サーバ側で必要なモジュールのインストール、Vue.jsやPythonの環境構築等はご自身で行ってください。
今回のコード
今回作成したプロジェクトはGitHub上にあります。
クローンしていただいて、自由にお使いください。
また、使用しているOpenCVは以下のリポジトリから取得しました。
早速APIを解説
さて、API側からざっくり解説します!
尚、環境構築やFlaskの立て方等は今回割愛します。
1.POSTによって画像を取得
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# ファイルを受け取る方法の指定 @app.route('/face', methods=['GET', 'POST']) def uploads_file(): # リクエストがポストかどうかの判別 if request.method == 'POST': print(request.files) # ファイルがなかった場合の処理 if 'file' not in request.files: return 'file not found in request' # データの取り出し file = request.files['file'] # ファイルが空の時の処理 if file.filename == '': return 'file name is null' # ファイルのチェック if file and allowed_file(file.filename): # 危険な文字を削除(サニタイズ処理) filename = secure_filename(file.filename) # ファイルの保存 file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) # 画像を変換 convert_img, people = face_recognition(filename) # JSONを返す return jsonify('{ \"img\": \"' + str(convert_img) + '\", \"people\": \"' + str(people) + '\"}') return 'error' |
基本的にコメントアウト読んでしまえば内容がわかるようにしているつもりなので、改めて説明するまでもないですが…
以下の場合はエラーとなり、検出処理をしないようになっています。
- リクエストがPOSTでないとき
- ファイルがPOSTされていないとき
- ファイル自体が空のとき
- ファイルの拡張子が gif , jpg , png のいずれでもないとき
以上のチェックが終わり、こちらの指定した画像ファイル形式であれば以下の処理を行います。
- サニタイズ処理
- ファイルをディレクトリに保存(/src/upload)
- 顔検出
- JSONを返す
尚、サニタイズ処理とは特殊な文字(例えば & や > など)を無効化することで意図した動きをしないように処理することです。
▼詳しくはこちら
2.顔検出
顔を検出する処理をします。
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# 顔認識 def face_recognition(target_img): target_img = UPLOAD_FOLDER + target_img # 入力画像を読み込み img = cv2.imread(target_img) # カスケード型識別器の読み込み cascade = cv2.CascadeClassifier("./lib/opencv/data/haarcascades/haarcascade_frontalface_default.xml") # グレースケール変換 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 顔領域の探索 face = cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=3, minSize=(30, 30)) # 顔領域を赤色の矩形で囲む for (x, y, w, h) in face: cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 200), 3) # 結果の出力 output_file = RESULT_FOLDER + str(time.time()) + ".png" cv2.imwrite(output_file, img) return output_file, len(face) |
まず、この関数では引数として対象画像のパスを取得し、戻り値として検出結果画像のパスと検出数を返します。
さて、こちらも基本的にコメントアウトされている通りです。
尚、こちらのカスケード型識別器に関しては、冒頭でも書いたOpenCVのリポジトリを取得し、そのパスを指定してください。(私の場合は ./lib に格納しています。)
続いて、グレースケール変換を行います。
何故グレースケールに?という点に関しては、計算量・作業メモリの削減により高速で処理ができるためです。尚、カラースケールでも顔検出は可能です。
その後は、カスケード型識別器を用いて顔領域を探索し、赤色の枠で囲むだけです。
最後に画像をディレクトリ(src/face)へ出力し、そのパスと顔の検出数を返して終了です。
続いてクライアント側
クライアント側はVue.jsで作りました。
画像をAPIにPOSTできればいいだけなので、フレームワークはどれでもいいのですが、今回は最近Vue.jsにハマっているという理由でVue.jsで書きました。
ざっくり解説
こちらのやってることを一言でまとめると「画像を選択してPOSTして結果を表示する」というだけです。
以下の関数ではまず選択された画像をプレビューとして表示します。
33 34 35 36 37 38 39 40 41 |
setImage() { // 画像のプレビュー関数 const files = this.$refs.file; const fileImg = files.files[0]; // 選択された File の情報を保存しておく if (fileImg.type.startsWith("image/")) { this.target_image = window.URL.createObjectURL(fileImg); this.name = fileImg.name; } }, |
続いて、この関数ではアップロード時の処理を書いています。
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
upload: function () { // アップロード処理 // FormData を利用して File を POST する let formData = new FormData(); formData.append('file', this.$refs.file.files[0]); const config = { headers: { 'content-type': 'multipart/form-data' } }; const self = this; // APIへPOST axios .post(this.APIdomain + 'face', formData, config) .then(function (response) { self.response = JSON.parse(response['data']); }) .catch(function (error) { console.log(error); }) }, |
フォームのデータとして送信するためにヘッダやファイルを格納し、axiosを使ってAPIにPOSTしています。
返ってきた値を変数に格納し、画像を画面に表示しています。
最後に
基本的にコメントアウトで解説が入ってるので改めてブログに書くこともそこまでありませんでした。何か不明点等ございましたら、コメントもしくはTwitterまでお問い合わせください。
尚、それぞれの利用方法に関しては各READMEをご覧ください。
今回のブログ曲
今回投稿中に聴いていた曲はこちら