Help us understand the problem. What is going on with this article?

【Python】顔画像をPOST 、 顔検出 、 canvasで顔にお絵かき【JS】

※こちらに移行しました。 ← 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
など

アプリケーション

face_detection
こんなイメージ。
とりあえず、顔に枠線を書いている。
画像の描画、顔へのお絵かきは全てJSで実装。
サーバ側は顔検出するだけ。

下記にソース。

ディレクトリ構成

./
├── face_detect.py # 顔検出の処理
├── index.py       # リクエスト処理
├── templates      # html(テンプレート)
│   └── index.html 
└── tmp            # アップロードされた画像格納ディレクトリ
    └── sample.jpg

フロント

ファイルが選択されたら、canvasに画像を描画。
ボタン押下時にPOST。
POST処理はJSで。

html

index.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を使ってみた。

index.js
(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などで実装。

顔検出のサンプルソース - OpenCV

index.py

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

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アプリケーション構築

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away