WEBブラウザに画像ファイルをドロップしてBase64エンコードする処理と、Base64エンコードテキストを受け取って、学習済モデルを使って何の画像かを識別する処理を組み合わせて、簡単なデモンストレーションを作ってみます。
はじめに
ブログで別々の記事に書いている3つを組み合わせて、簡単なデモを作ってみます。
その3うとは。
画像識別するクラス
識別結果を英語→日本語化するクラス
ドラッグ&ドロップで画像ファイルを受け取り、Base64形式にエンコードしてHTTPRequest経由でPythonプログラムに渡すJavaScript
です。
これを組み合わせて。
- 画像ファイルをドラッグ&ドロップで受け取り、Base64形式にエンコードする。
- エンコードしたBase64テキストをSubmitでPythonプログラムに渡す。
- 受け取ったBase64テキストを画像にデコードする。
- 画像を受け取り物体名を推論する。
- 結果として得た物体名を日本語に変換する。
- 画像と推論した物体名を画面に表示する。
という動きをするデモ画面を作ります。
画像識別クラスに日本語化とBase64形式いたフェースを追加する
上記のうち、不足しているのが「3.受け取ったBase64テキストを画像にデコードする」機能なので、それを追加します。
Base64形式から画像にデコードする処理のみ抜粋します。
PIL.Image と BytesIOのライブラリを使えば1行で書けるのですが・・。
import PIL.Image as Image
from io import BytesIOimg = Image.open(BytesIO(base64.b64decode(base64text))).resize(self.IMAGE_SHAPE)
上記のIMAGE_SHAPEは「 (224, 224) 」のように、画像の縦横サイズをセットしていて、適当なサイズの画像でもリサイズできるようにしてます。
あと。
評価結果の日本語化ですね。
これは上記の記事で作ってみたクラスを使ってこんな感じにします。
from . import to_japanese_ilsvrc2012 as toj
self.translator = toj.Ilsvrc2012Japanese()
ans['jp'] = self.translator.convert(results[0][1])
このresults[0][1]は英語名の文字列をあらわします。
これらを組み込んで、拡張したクラス全体のソースです。
vgg16_imagenet.py
import tensorflow as tf import numpy as np import PIL.Image as Image from io import BytesIO import base64 from . import to_japanese_ilsvrc2012 as toj class Vgg16k: def __init__(self): self.model = tf.keras.applications.vgg16.VGG16( weights='imagenet', include_top=True) self.translator = toj.Ilsvrc2012Japanese() self.IMAGE_SHAPE = (224, 224) def __predict(self, img): x = tf.keras.preprocessing.image.img_to_array(img) x = np.expand_dims(x, axis=0) x = tf.keras.applications.vgg16.preprocess_input(x) pred = self.model.predict(x) results = tf.keras.applications.vgg16.decode_predictions(pred, top=1)[ 0] ans = {} ans['pp'] = '{:.2f}'.format(results[0][2] * 100.0) + '%' ans['jp'] = self.translator.convert(results[0][1]) ans['en'] = results[0][1] return ans def predict_from_path(self, imgpath): img = tf.keras.preprocessing.image.load_img( imgpath, target_size=self.IMAGE_SHAPE) return self.__predict(img) def predict_from_base64(self, base64text): img = Image.open( BytesIO( base64.b64decode(base64text))).resize( self.IMAGE_SHAPE) return self.__predict(img) if __name__ == '__main__': def base64encode(file_name): target_file = file_name with open(target_file, 'rb') as f: data = f.read() encoded_base64_text = base64.b64encode(data) return encoded_base64_text m = Vgg16k() a = m.predict_from_base64(base64encode('.\\tf20\\test.jpg')) print(a['jp'])
ほぼ、以下の記事のクラスに上記の追加処理を書き足しただけです。
Djangoプログラムにロジックを追加する
ドラッグ&ドロップを受けてBase64変換してサブミットするまでの処理は、以下の記事で書いていものから変更はありません。
唯一変更するのは「views.py」です。
ここに、画像識別ロジックを組み込む必要があります。
そこだけ抜粋してみます。
この「img」には、JavaScriptでエンコードしたBase64形式のテキストデータがはいってます。
JavaScriptでエンコードしたBase64テキストには、例えばJPEGなら画像データの前に識別情報として「data:image/jpeg;base64,」がついてます。
これをそのまま渡すとデコードする時にエラーになるので、画像ファイルの種類を判定して余分な箇所を消してます。
いちおう。
インプットになるJavaScript側がそうなっているので、PNG形式も書いてますが・・、こちらは画像識別のテストをしてません。
なので、PNG画像だとうまくいかない可能性があります(苦笑)。
自分が、デモとかで使うときはJPEGファイル固定でやってます。
ちゃんと修正しろよ!・・とお叱りが聞こえそうですが(笑)、そのうち、暇ができたら・・ということで、ご容赦を。
上記を組み込んだ「views.py」です。
views.py
HTMLを少しだけ修正して実行です
上記以外は、ほぼ、こちらの内容を踏襲していますが、 HTMLを少しだけ修正します。
画像の下に評価結果の文字列を表示するエリアをつけくわえるだけですが。
dragdrop.html
{% extends 'base.html' %} {% load static %} {% bootstrap_css %} {% bootstrap_javascript jquery='full' %} {% load widget_tweaks %} {% block header %} <link rel="stylesheet" href="{% static "css/dragdrop.css" %}"></link> {% endblock %} {% block title %} サンプルその1 {% endblock %} {% block content %} <div class="container"> <form action="" method="post">{% csrf_token %} {% if base64text %} <h4>ドロップされた画像</h4> <div class="form-group row my-4"> <img src="{{ base64text }}" /> </div> <div class="form-group row my-4"> <h4>英語名:{{ enlabel }}</h4> </div> <div class="form-group row my-4"> <h4>日本語:{{ jplabel }}</h4> </div> <div class="form-group row my-4"> <h4>確率は:{{ pplabel }}</h4> </div> <a href="{% url 'dragdrop' %}"><h2>もう一回</h2></a> {% else %} <h4>ドラッグエリア</h4> <div class="form-group row my-4"> <div id="dragandrophandler" class="border col-lg-9">ここにJPEGまたはPNGファイルをドロップ</div> </div> <div class="form-group row my-4"> <div class="col-lg-2"> <button type="submit" class="btn btn-primary">サブミット</button> </div> <div class="col-lg-8"></div> </div> <div id="imgdiv" class="form-group row my-4"> <img src="" /> </div> <div class="form-group row my-4" hidden> {{form.areaone|add_class:"form-control"}} </div> {% endif %} </form> </div> <script type="text/javascript" src="{% static "js/dragdrop.js" %}"></script> {% endblock %}
さて、変更はこれくらいで、実行してみます。
実行の仕方はこちらの記事と同じです。
あとは。
実行した画面イメージだけ。
画像をドロップして。
確認して。
サブミットすると。
まあ、いい感じじゃないですかね。
今回はこんなところで。
ではでは。
※これは2019/1/30にアップした記事のリライトです。