やっていきましょう (´・ω・`)これで一旦終わりにします。
views.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
from django.shortcuts import render from django.shortcuts import redirect # 追加 # Httpレスポンスをするためのモジュール from django.http import HttpResponse # テンプレートファイルを読み込む from django.template import loader # forms.pyで定義したPhotoFormクラスを呼び出す from .forms import PhotoForm # models.pyで定義したPhotoクラスを呼び出す from .models import Photo #追加 def index(request): # webサイトのindex.html に相当する部分 # 構造がとても複雑だが、こういうものだと諦めること。 # 引数としてrequestを受け取る # index.htmlを読み込み # オブジェクトtemplateを定義する template = loader.get_template('carbike/index.html') #追加 # forms.pyのクラスPhotoFormのインスタンスを作成して、 # ディクショナリcontextを定義しておく。 context = {'form':PhotoForm()} # オブジェクトtemplateのrender関数に # フォームcontextと引数requestを入れて # HttpResponseを返す return HttpResponse(template.render(context, request)) def predict(request): # POSTでなければindexへ強制遷移します。 if not request.method =='POST': # return なにも返さない print("redirect('carbike:index')が動きます") redirect('carbike:index') # < これ以降はPOSTでデータが取得できている状態 > # PhotoFormインスタンスを作成する form = PhotoForm(request.POST, request.FILES) # 念の為、データが空だったらエラーを返す if not form.is_valid(): raise ValueError('Formが不正です') # 変数Photo に 画像データを格納する。 # (別途Photoクラスを作成する必要がある。models.py参照) photo = Photo(image=form.cleaned_data['image']) print("変数photoにPhoto(image=form.cleaned_data['image'])を格納完了") print(photo) # 判断結果のリターンをmodels.pyのpredictメソドから取得する predicted, percentage = photo.predict() # 表示する内容 template = loader.get_template('carbike/result.html') context = { 'photo_name': photo.image.name, 'photo_data': photo.image_src(), 'predicted': predicted, 'percentage': percentage, } return HttpResponse(template.render(context, request)) |
form.py
1 2 3 4 5 6 |
# フォームを生成するモジュール from django import forms class PhotoForm(forms.Form): # 画像をアップロードする機能 image = forms.ImageField(widget=forms.FileInput(attrs={'class':'custom-file-input'})) |
model.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
from django.db import models # Create your models here. import numpy as np #tfよりも先に行っておく # 追加モジュール import io, base64 #画像ファイルを数値に変換 ブラウザへ画像を戻す import tensorflow as tf #追加 # vgg16_predict.pyからモジュールをコピペ import numpy as np from PIL import Image import sys from tensorflow import keras from tensorflow.keras.models import load_model # 推論するセッションの初期化を行っておく graph = tf.get_default_graph() class Photo(models.Model): # models.Modelを拡張してPhotoクラスを作成する # 画像データをアップロードするディレクトリを指定する # myproject直下に /media/photos の2階層ディレクトリを新規作成すこと。 image = models.ImageField(upload_to='photos') # < vgg16 パラメータ > # 画像サイズ IMAGE_SIZE = 224 # モデルファイルを読み込む(別途フォルダ作成が必要) # ./car_motorbike/ml_models/vgg16_trans.h5 を作成しておくこと。 MODEL_FILE_PATH = './car_motorbike/ml_models/vgg16_trans.h5' print('モデルファイルパス読み込み完了') # vgg16_predict.pyからコピペ # 出力結果のクラスを定義 classes = ["car", "motorbike"] num_classes = len(classes) # クラス数を定義 def predict(self): print('Photo.predictメソド開始') # 引数から画像ファイルを参照して読み込む # クラスメソドとして定義してあげる # モデルの初期化 model = None # 毎回同じモデルのセッションにデータを投入するためグローバル変数とする global graph with graph.as_default(): # 予め定義しておいたMODEL_FILE_PATH からモデルを定義できるようにする model = load_model(self.MODEL_FILE_PATH) # 画像データを格納する img_data = self.image.read() # 画像データをメモリ上に保持して、ファイルのようにアクセスできるよう、 # バイナリデータに変換する img_bin = io.BytesIO(img_data) # pillow の Imageでバイナリデータを開く image = Image.open(img_bin) # 以下、vgg16_predict.pyからコピペ # 画像サイズを定義(VGG16に合うように) # image_size = 224 クラスとして定義済み # 画像ファイルをコマンドラインの第2引数から開く # 今回はimag_binから開くので不要 # image = Image.open(sys.argv[1]) # 画像ファイルをRGBに変換する image = image.convert('RGB') # 画像を同じサイズに揃える # image = image.resize((image_size, image_size)) image = image.resize((self.IMAGE_SIZE, self.IMAGE_SIZE)) # RGB値をnumpy配列に変換する data = np.asarray(image) # データを正規化する data = data / 255.0 # リストにappendしてnumpy配列にする X = [] X.append(data) X = np.array(X) # VGG16_transモデルをロードする # model = load_model('./vgg16_trans.h5') すでに上記で行っているので不要 # VGG16_transモデルにデータを入れて予測結果を返す # スコアが配列で帰ってくるので[0]番目をつける result = model.predict([X])[0] # 最も大きい数字を採用する predicted = result.argmax() percentage = int(result[predicted] * 100) # プリントする print(self.classes[predicted], percentage) # 判断結果とパーセンテージをreturnする return self.classes[predicted], percentage def image_src(self): # views.pyの 'photo_data': photo.image.src() へ連動させる with self.image.open() as img: base64_img = base64.b64encode(img.read()).decode() return 'data:' + img.file.content_type + ';base64,' + base64_img |
result.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<!-- リザルトページを作成します。--> <!-- テンプレートであるbase.htmlに対して 拡張する宣言をします。--> {% extends 'carbike/base.html' %} <!-- タイトル title をbase.htmlに埋め込みます。--> {% block title %}判定結果{% endblock %} <!-- 内容 content をbase.htmlに埋め込みます。--> {% block content %} <div> <!-- classはbootstrap記法で文字間の調整になる mtマージントップ mb マージンボトム 境界線底部 --> <h4 class="mt-4 mb-5 border-bottom">車とバイクを判別します。</h4> <!-- 判別結果と推定確率のテーブルを設置する --> <table class='table'> <tbody> <tr> <td>ファイル名</td> <td>{{ photo_name }}</td> </tr> <tr> <td>画像ファイル</td> <td><img class='preview-img' src={{ photo_data }}></img></td> </tr> <tr> <td>推定ラベル</td> <td>{{ predicted }} </td> </tr> <tr> <td>推定確率</td> <td>{{ percentage }} %</td> </tr> </tbody> </table> <!-- 戻るボタンを設置する --> <a href="{% url 'carbike:index' %}" class="btn btn-primary">画像選択メニューに戻る</a> </div> {% endblock %} |
実行してみる
conda activate djangoai
cd anaconda_projects
cd djangoai
cd myproject
pythin manage.py runserver
url:http://127.0.0.1:8000/carbike
おわり
いやー疲れました、djangoは難しい。小さなアプリでも多くのファイル間を連携させないと動いてくれないんですね。
今後は、マッチングアプリや遠隔監視カメラなんてものを作ってみたいと思います。