[python] Webカメラからキャプチャした画像をサーバに送信して保存する

# 概要
以下を実装します。
・ブラウザでWebカメラを起動
・カメラ画面の画像をキャプチャ
・キャプチャ画像をサーバに送信して保存

# フォルダ構成

```
/home/capture_img
|--run.py
|--app
|  |--api.py
|  |--service.py
|  |--static
|  |  |--main.js
|  |--templates
|  |  |--index.html
|--images #キャプチャした画像の保存先
```

# ソースコード

## run.py
```python:run.py
from app.api import api

if __name__ == '__main__':
    api.run(host='0.0.0.0', port=8000)
```

## app/api.py
```python:app/api.py
from flask import Flask, request, make_response, render_template, url_for

from . import service

api = Flask(__name__)

@api.route('/', methods=['GET'])
def index():
    return render_template('index.html')

@api.route('/capture_img', methods=['POST'])
def capture_img():
    msg = service.save_img(request.form["img"])
    return make_response(msg)
```

## app/service.py
```python:app/service.py
import base64
import numpy as np
import cv2

def save_img(img_base64):
    #binary <- string base64
    img_binary = base64.b64decode(img_base64)
    #jpg <- binary
    img_jpg=np.frombuffer(img_binary, dtype=np.uint8)
    #raw image <- jpg
    img = cv2.imdecode(img_jpg, cv2.IMREAD_COLOR)
    #デコードされた画像の保存先パス
    image_file="/home/capture_img/images/img0000.jpg"
    #画像を保存
    cv2.imwrite(image_file, img)
    return "SUCCESS"
```

## templates/index.html
```html:templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>get_capture</title>
</head>
<body>
    <h3>スペースキー押下でキャプチャ取得</h3>
    <video id="video" width="640" height="480" autoplay></video>
    <canvas id="canvas" class="canvas-wrapper"></canvas>
</body>

<script src="{{url_for('static', filename='main.js')}}"></script>

<style>
    /* canvasは非表示にする */
    #canvas {
        display: none !important;
    }
</style>
</html>
```

## static/main.js
```javascript:static/main.js
var video = document.getElementById('video');
// getUserMedia()でカメラ映像の取得
var media = navigator.mediaDevices.getUserMedia({ video: true });
//リアルタイム再生（ストリーミング）を行うためにビデオタグに流し込む
media.then((stream) => {
    video.srcObject = stream;
});

var canvas = document.getElementById('canvas');
canvas.setAttribute('width', video.width);
canvas.setAttribute('height', video.height);

video.addEventListener(
    'timeupdate',
    function () {
        var context = canvas.getContext('2d');
        context.drawImage(video, 0, 0, video.width, video.height);
    },
    true
);

// スペースキー押下時にキャプチャ取得を実行するリスナーを設定
document.addEventListener('keydown', (event) => {
    var keyName = event.key;
    if (keyName === ' ') {
        console.log(`keydown: SpaceKey`);
        context = canvas.getContext('2d');
        // 取得したbase64データのヘッドを取り除く
        var img_base64 = canvas.toDataURL('image/jpeg').replace(/^.*,/, '')
        captureImg(img_base64);
    }
});

var xhr = new XMLHttpRequest();

// キャプチャ画像データ(base64)をPOST
function captureImg(img_base64) {
    const body = new FormData();
    body.append('img', img_base64);
    xhr.open('POST', 'http://localhost:8000/capture_img', true);
    xhr.onload = () => {
        console.log(xhr.responseText)
    };
    xhr.send(body);
}
```

# 必要なモジュールのインストール
```
root@ed9f7bedad16:/# pip install opencv-python flask
```

# 実行
```
root@ed9f7bedad16:/# python /home/capture_img/run.py
 * Serving Flask app "app.api" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://0.0.0.0:8000/ (Press CTRL+C to quit)

```

# 動作確認

## http://localhost:8000にアクセス & スペースキー押下

![スクリーンショット 2020-10-16 23.50.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281645/7a28833a-fb95-f488-5f76-19b3424c6647.png)

```
root@ed9f7bedad16:/# ls /home/capture_img/images/
img0000.jpg
```
キャプチャ画像が保存されました。
![img0000.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281645/6bf8c2be-acb1-bf3c-8c1e-d6fba521b0cf.jpeg)



以上です。