EnsekiTT Blog

EnsekiTTが書くブログです。

FlaskとOpenCVで投稿された画像をOpenCVで加工して返す話

つまりなにしたの?

Flaskで作られたWebサーバに画像が投稿されたらOpenCVで加工して結果をブラウザに表示するようにした。
f:id:ensekitt:20180719224643j:plain

環境

 % python --version
Python 3.6.4
 % pip freeze
Flask==1.0.2
opencv-python==3.4.0.12
numpy==1.14.1

大まかな手順

  • Indexでフォームを表示する
  • フォームで追加されたファイルを受け取る
  • OpenCVで読み込む
  • ファイルを加工する(縮小とか、色を変えたりとか)
  • 元画像と加工画像を保存する
  • 加工した画像と元画像のURLを返して元のフォームと一緒に表示する

ディレクトリ構造と実行方法

 % tree .
.
├── templates
│   └── index.html
├── uploads
└── app.py
  • 実行方法
python app.py

コード

アプリケーションサーバのコード

  • app.py
import os
import io
import time
import numpy as np
import cv2
from flask import Flask, render_template, request, redirect, url_for, send_from_directory, session
from werkzeug import secure_filename
app = Flask(__name__)

UPLOAD_FOLDER = './uploads'
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'PNG', 'JPG'])
IMAGE_WIDTH = 640
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['SECRET_KEY'] = os.urandom(24)

def allowed_file(filename):
    return '.' in filename and \
        filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/send', methods=['GET', 'POST'])
def send():
    if request.method == 'POST':
        img_file = request.files['img_file']

        # 変なファイル弾き
        if img_file and allowed_file(img_file.filename):
            filename = secure_filename(img_file.filename)
        else:
            return ''' <p>許可されていない拡張子です</p> '''

        # BytesIOで読み込んでOpenCVで扱える型にする
        f = img_file.stream.read()
        bin_data = io.BytesIO(f)
        file_bytes = np.asarray(bytearray(bin_data.read()), dtype=np.uint8)
        img = cv2.imdecode(file_bytes, cv2.IMREAD_COLOR)
        # とりあえずサイズは小さくする
        raw_img = cv2.resize(img, (IMAGE_WIDTH, int(IMAGE_WIDTH*img.shape[0]/img.shape[1])))

        # サイズだけ変えたものも保存する
        raw_img_url = os.path.join(app.config['UPLOAD_FOLDER'], 'raw_'+filename)
        cv2.imwrite(raw_img_url, raw_img)

        # なにがしかの加工
        gray_img = cv2.cvtColor(raw_img, cv2.COLOR_BGR2GRAY)

        # 加工したものを保存する
        gray_img_url = os.path.join(app.config['UPLOAD_FOLDER'], 'gray_'+filename)
        cv2.imwrite(gray_img_url, gray_img)

        return render_template('index.html', raw_img_url=raw_img_url, gray_img_url=gray_img_url)

    else:
        return redirect(url_for('index'))

@app.route('/uploads/<filename>')
def uploaded_file(filename):
    return send_from_directory(app.config['UPLOAD_FOLDER'], filename)

if __name__ == '__main__':
    app.debug = True
    app.run()

テンプレートファイル(index.html)のコード

  • index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>File</title>
</head>
<body>
  <form method="post" action="/send" enctype="multipart/form-data">
    <input type="file" id="img_file" name="img_file">
    <input type="submit" value="送信">
  </form>
  <p>
  {% if raw_img_url %}
    <img src="{{ raw_img_url }}">
  {% endif %}
  {% if gray_img_url %}
    <img src="{{ gray_img_url }}">
  {% endif %}
  </p>
</body>
</html>

実行結果

f:id:ensekitt:20180719224452p:plain:w600
名古屋の辛いラーメンを白黒画像にしてやることができました。

クリエイティブ・コモンズ・ライセンス
この 作品 は クリエイティブ・コモンズ 表示 4.0 国際 ライセンスの下に提供されています。