ブラウザ上でイメージを手描きして、それをPython等の後続処理で使えるようにBase64形式に変換して、textareaにセットするところまでやってみます。
はじめに
Python等で分類器などを作ってWEBブラウザ上でデモなどをするとき、必要になるのが「画像イメージをWEBブラウザ上で取得して、SUBMITでPython等サーバー側の処理に渡せる形式(Base64)に変換する」という処理です。
今までも、散発的には記事にしてるのですが、自分でもど忘れした時に、あちらこちら見るのが面倒になってきたので、ちょこっとまとめておこうと思います。
今回は、画面上で手で描画したイメージを取り込むパターンでやってみます。
Django2.0+bootstrap4のベース(jQuery付き)です。
手書き描画にはライブラリを使います
ブラシを使ったFREE DRAWINGは、HTML5のCANVASを使えば実現できるのですが、素で扱うと少々面倒くさいです。
なので、今回はCANVASの扱いを簡単にしてくれる「Fabric.js」を使います。
現状、ブラシの種類も少なくて、基本的な機能だけ提供されていますが(Free Drawingは・・です。それ以外は豊富です)、今回はそれで充分ですから。
一応、以下を引用しておきます。
In the near future, we are planning to add more options for free drawing — various versions of a brush (e.g. spray-like or chalk-like). Also custom brush patterns, and an option to extend with your own, similar to Fabric image filters.
近い将来、さまざまなバージョンのブラシ(例えばスプレーまたはチョークのような)など、フリー描画のオプションを追加する予定です。
また、カスタムブラシパターンと、ファブリックイメージフィルタと同様に独自に拡張するオプションもあります。
まずはHTML部分から
Django2.0のテンプレート機能を使います。
Base.htmlを読み込んで使う、以下の記事のやり方を踏襲しています。
埋め込む部分のHTMLです。
{% extends 'base.html' %} {% load static %} {% bootstrap_css %} {% bootstrap_javascript jquery='full' %} {% load widget_tweaks %} {% block header %} <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.3/fabric.js"></script> {% endblock %} {% block title %} サンプルその1 {% endblock %} {% block content %} <div class="container"> {% if base64text %} {% else %} <div class="row my-4"> <canvas id="canvas" style="border:solid 1px red" width="300" height="300"></canvas> </div> {% endif %} <form action="" method="post">{% csrf_token %} {% if base64text %} <div class="form-group row my-4"> <img src="{{ base64text }}" /> </div> <a href="{% url 'canvas01' %}"><h2>もう一回</h2></a> {% else %} <div class="row my-4"> <div class="col-lg-2"> <button type="submit" class="btn btn-primary">サブミット</button> </div> <div class="col-lg-2"> <input type="button" id="tobase64btn" onclick="canvas_to_base64()" value="Base64変換" /> </div> <div class="col-lg-2"> <input type="button" id="clearbtn" onclick="canvas_clear()" value="クリアー" /> </div> <div class="col-lg-4"></div> </div> <div class="form-group row my-4"> {{form.areaone|add_class:"form-control"}} </div> {% endif %} </form> </div> <script type="text/javascript" src="{% static "js/canvas01.js" %}"></script> {% endblock %}
補足します。
まず、Fabric.jsを使うためにライブラリを読み込みます。
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.3/fabric.js">
手描きするためのエリアはCanvasで定義します。
<canvas id="canvas" style="border:solid 1px red" width="300" height="300"></canvas>
JavaScript内でID指定で参照参照しているため、idは「canvas」から変更できません。
Canvasに描いた図を画像フォーマット(JPEG)にしてBase64変換するJavaScriptの関数をキックするボタン。
<input type="button" id="tobase64btn" onclick="canvas_to_base64()" value="Base64変換" />
および、Canvas内をクリアするボタン
<input type="button" id="clearbtn" onclick="canvas_clear()" value="クリアー" />
を追加しています。
{% if base64text %}以下の部分が、画像を描いてBase64変換後に、「サブミット」ボタンを押した後に表示する画面。
{% else %}以下の部分が、初期表示です。
とりあえず、初期表示画面のイメージです。
Canvasを操作するJavaScript
上記で読み込んでいるJavaScriptです。
canvas01.js
var canvas = new fabric.Canvas('canvas', { isDrawingMode: true }); fabric.Object.prototype.transparentCorners = false; canvas.freeDrawingBrush = new fabric['PencilBrush'](canvas); canvas.freeDrawingBrush.color = "black"; canvas.freeDrawingBrush.width = 18; function canvas_clear() { canvas.clear(); $("#resultbase64").val(''); } function canvas_to_base64() { $("#resultbase64").val(canvas.toDataURL("image/jpeg")); }
fabric.Canvasオブジェクトを生成し、isDrawingMode: trueとするだけで、Free Drawingができるようになります。
あとは、ブラシオブジェクトを生成して、色と太さをセットしているだけです。
超簡単です(笑)
HTML5のCanvasを使えば、画像をjpegフォーマットにして、Base64形式に変換する処理も「canvas.toDataURL」一発でできます。
もちろん、PNG形式にもできますが、今回はjpegにしました。
TextareaにBase64形式テキストをわざわざセットしているわけ
入力した画像をJavaScriptで変換しただけでは、djangoのコントローラ側(Pythonプログラム)に渡せないためです。
requestに乗せて、python側で「request.POST["areaone"]」のように受け取るためには、formを使う必要があります。
なので、一旦JavaScriptでBase64形式にしたものを、formで定義したtextareaにセットして、Submitボタンでtextareaの内容としてrequestにのせてます。
今は検証用なので表示して、かつ、わざわざボタン操作で変換しています。
本来は、textareaをHidden(隠しフィールド)にして見えなくする必要があります。
なお。
textareaはdjangoフォームを使っています。
forms.pyに以下のように書いています。
from django import forms class UserForm(forms.Form): areaone = forms.CharField( label='', widget=forms.Textarea( attrs={ 'id': 'resultbase64', "rows": 20, "cols": 10, } ) )
これをHTMLでは以下のようにうけてます。
{{form.areaone|add_class:"form-control"}}
djangoのコントローラです
実行できるように、djangoのviews.pyに以下のように書きます。
from django.shortcuts import render from . import forms from django.template.context_processors import csrf def freedraw(request): if request.method == 'POST': c = { 'base64text': request.POST["areaone"], } else: form = forms.UserForm(label_suffix=':') c = {'form': form} c.update(csrf(request)) return render(request, 'canvas01.html', c)
最後にurls.pyに以下を追加して、demo02でアクセスできるようにします。
path('demo02/', views.freedraw, name='canvas01'),
これでOK。
実行してみます
プロジェクトフォルダ(manage.pyがあるフォルダ)をカレントにして。
python manage.py runserver
それで、例えば「http://localhost:8000/demo02/」のようにアクセスします。
初期表示画面で、手書きの数字を書いて「Base64変換」ボタンを押した状態です。
ここから「サブミット」を押すと。
ちゃんと手書き数字画像がrequestで渡されて、表示できてます。
OKです。
とりあえず、今回はこんなところです・・が、ちょっとだけおまけがあります。
おまけ
Chromeのデベロッパーツールを使ってデバッグしていたら、コンソールに表示されて、どうしても消えないエラーメッセージがありました。
こういうやつです。
Unchecked runtime.lastError: The message port closed before a response was received.
気持ち悪かったのですが、以下の記事によれば、無視してかまわないみたいです。
まめに調べてくださってる方がいるんですね。
これはよい情報だと思ったので、リンクをはっておきます。
ではでは。