※こちらに移行しました。 ← 2018/11/24
概要
「顔検出をして、顔に絵を描く」というもの。
具体的には、
顔写真の画像をAPIサーバへアップロードし、顔検出を行う。
サーバは顔座標を返し、それを元にcanvasでお絵かきする。
参考
【Python】顔画像をPOST 、 顔検出 、 canvasで顔にお絵かき【JS】
【Python】Apache + WSGI + Flask でWebアプリケーション構築
環境
フロント
html + JS + canvas
axios.jsでAPIを叩く
サーバサイド
Ubutnu 16.04
Python + Flask
OpenCV 3.2
など
アプリケーション
こんなイメージ。
とりあえず、顔に枠線を書いている。
画像の描画、顔へのお絵かきは全てJSで実装。
サーバ側は顔検出するだけ。
下記にソース。
ディレクトリ構成
./
├── face_detect.py # 顔検出の処理
├── index.py # リクエスト処理
├── templates # html(テンプレート)
│ └── index.html
└── tmp # アップロードされた画像格納ディレクトリ
└── sample.jpg
フロント
ファイルが選択されたら、canvasに画像を描画。
ボタン押下時にPOST。
POST処理はJSで。
html
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.js"></script>
</head>
<body>
<div class="container">
<canvas id="canvas"></canvas>
<hr>
<label>
画像
<input type="file" class="form-control" id="image">
</label>
<button id="run">
submit
</button>
</div>
<script src="index.js">
</script>
</body>
</html>
js
requestを投げるのにaxiosを使ってみた。
(function(){
document.getElementById("run").addEventListener("click", function(){
var params = new FormData();
var file = document.getElementById("image").files[0]
params.append('image', file)
axios.post('/api', params).then(function(response) {
response.data.forEach((e) => {
var v = e.face
drawRect(v.x, v.y, v.w, v.h)
e.eyes.forEach((eye) => {
eye.x += v.x
eye.y += v.y
drawRect(eye.x, eye.y, eye.w, eye.h)
})
})
}).catch(function(error) {
alert("error")
});
})
document.getElementById("image").addEventListener("change", function(e) {
var file = e.target.files;
var reader = new FileReader();
reader.readAsDataURL(file[0]);
reader.onload = function() {
var source = reader.result;
drawImage(reader.result)
}
}, false);
function drawImage(src) {
var canvas = document.getElementById("canvas")
var context = canvas.getContext('2d')
var image = new Image()
image.src = src;
image.onload = function() {
canvas.width = image.width
canvas.height = image.height
context.drawImage(image, 0, 0)
}
}
function drawRect(x, y, w, h) {
var canvas = document.getElementById("canvas")
var context = canvas.getContext('2d')
context.rect(x, y, w, h);
context.stroke();
}
})()
メモ
drawRectが顔画像の描画処理を行なっている。
サーバ
サーバサイドはPython + Flaskで実装。
顔画像処理はOpenCV、numpyなどで実装。
index.py
import os,sys
from flask import Flask, render_template, request, jsonify
from jinja2 import FileSystemLoader
import base64
from face_detect import get_facepos
app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 1 * 1024 * 1024
app.config['UPLOAD_FOLDER'] = './tmp'
@app.route("/", methods=['GET'])
def get_index():
return render_template('index.html')
@app.route("/api", methods=['POST'])
def face_detect():
img = request.files['image']
name = img.filename
path = os.path.join(app.config['UPLOAD_FOLDER'], name)
img.save(path)
face_pos = get_facepos(path)
return jsonify(face_pos)
face_detect.py
import numpy as np
import cv2
def get_facepos(img_path):
face_cascade = cv2.CascadeClassifier('/path/to/haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier('/path/to/haarcascades/haarcascade_eye_tree_eyeglasses.xml')
img = cv2.imread(img_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
result = []
for (x,y,w,h) in faces:
face_dst = {
"x": np.asscalar(x),
"y": np.asscalar(y),
"w": np.asscalar(w),
"h": np.asscalar(h)
}
eyes_dst = []
roi_gray = gray[y:y+h, x:x+w]
eyes = eye_cascade.detectMultiScale(roi_gray)
for (ex,ey,ew,eh) in eyes:
eye_pos = {
"x": np.asscalar(ex),
"y": np.asscalar(ey),
"w": np.asscalar(ew),
"h": np.asscalar(eh)
}
eyes_dst.append(eye_pos)
result.append({
"face": face_dst,
"eyes": eyes_dst
})
return result
メモ1
カスケードファイルであるhaarcascade_frontalface_default.xmlなどは、
フルパスではなくファイル名だけで読み込めるはずですが、
特殊な環境など、読めない環境では、パスを記載する。
メモ2
face_detect.pyで宣言している顔検出ご座標(x,y,w,h)
をそのままListに格納し、
JSONに変換して出力しようとすると、エラーが発生する。(int32がなんたら...って言ってた)
とりあえず、np.asscalar()
を使って回避することができた。
参考
【Python】顔画像をPOST 、 顔検出 、 canvasで顔にお絵かき【JS】
【Python】Apache + WSGI + Flask でWebアプリケーション構築