kerasで作った画像分類器に画像を読み込ませ、予測したラベルのidを返すアプリ作った。以前、rubyで作ったことがあるけど、今回はpython専用のフレームワークDjangoを使って作成。
画像分類器にはCNNを使ったので、GPUとか学習のところは割愛して、アプリ作成過程についてだけ書いてみる。
目次
1.アプリについて
2.メインのファイル
1.アプリについて
一通りの動作
デフォルトのホーム画面はこんな感じでシンプル
写真をアップロードして、クリックすると予測した確率が高いトップ3のラベルのidが表示される仕組み
アプリ名と構成ファイル
project名はimage_pred
アプリ名はmyapp
メインの構成ファイルは
・setting.py
・forms.py
・views.py
・index.html
・main.py
・urls.py
いろんなファイルを保存するディレクトリは、必要に応じて作成した。
・Mはmodels.pyでデータベースの操作
・Vはviews.pyで画面の表示を操作
・Cはurls.pyでアクセス関係の操作
今回はデータベースにデータを保存しなくていいので、models.pyは使わない。使うときはマイグレーションが必要で、使えるデータベースは任意に指定可能
便利なパッケージでdjango-cleanupがあった。データベースを使うならpipでインストールして、setting.pyのINSTALLED_APPSに入れておきたい
2.メインのファイル
まずパッケージは
・Pillow
・keras
・tensorflow
・opencv
とかをpipでインストール
setting.py
myapp作成後に、INSTALLED_APPSにmyappを追加。
画像をアップロードするので以下のディレクトリを設定
・MEDIA_ROOT
=>サーバから見たメディアルートの絶対パス
・MEDIA_URL
=>メディアファイル公開時のURLのプレフィクス。 メディアファイルのURLは「http://アプリのドメイン+MEDIA_URL+メディアファイル名」
# 一部掲載 import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) SECRET_KEY = '8bdf)xh2b4h!8#g$i0j_9pymjld*ov19!rpfgj2qsi6j)-$t9d' DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'myapp' # 追加 ] STATIC_URL = '/static/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media') MEDIA_URL = '/media/'
forms.py
ここは画像をアップロードする専用のフォームを作成するファイル
画像専用フォーム「forms.ImageField()」を使用
from django import forms class PhotoForm(forms.Form): image = forms.ImageField()
index.html
ここはメインのホーム画面を表示するhtml。主にしてる処理は
・main.pyで予測したラベルのidをhtmlの変数に埋め込み、レンダリング
・フォームの設定
・静的ファイル(CSS)の読み込み
など。htmlにpythonの変数を直接、埋め込めるのがDjangoの強み
{% load static %} <!doctype html> <html lang="ja"> <head> <meta charset="utf-8"> <title>img_prediction</title> <link rel="stylesheet" type="text/css" href="{% static 'myapp/css/style.css' %}" /> </head> <body> <p>{{pred}}</p> <table> <form action="{% url 'index' %}" method="POST" enctype="multipart/form-data"> {% csrf_token %} {{ form }} <tr><td></td><td><input type="submit" value="click" /></td></tr> </form> </table> </body> </html>
静的ファイルは{% load フォルダ名 %}を記述してCSSを読み込ませた。 staticファルダ内にstyle.cssが保存してある。javascriptとかも同じように読み込める
変数は{{pred}}の部分。
フォームはformタグ内に記述。画像の場合はenctype="multipart/form-data"を指定しなきゃだめ
main.py
コードはkerasで作成した学習済みの画像分類器(CNN)と予測結果を返す処理。学習済みの重み、id用のcsvファイルは専用フォルダを別途作成して、保存してる。
# pred関数のみ def pred(img_path): sess = tf.Session() K.set_session(sess) cnt=pd.read_csv('/Users/d/image_pred/myapp/weight_dir/cnt.csv') cnt=cnt.drop('Unnamed: 0', axis=1) cnt=cnt.drop('cnt', axis=1) dic = {} for i, v in cnt.iterrows(): dic.setdefault(v['index'], []).append([v['brand_id'], v['model_id'], v['cate_id']]) x=np.asarray(Image.open(img_path)) x = cv2.resize(x, (100, 100)) x = np.expand_dims(x, axis=0) image = preprocess_input(x) test_model = InceptionResNetV2(include_top=True) test_model.load_weights('/Users/d/image_pred/myapp/weight_dir/incep_model.h5') test_model.compile(optimizer=SGD(lr=0.01, momentum=0.9, decay=0.001, nesterov=True), loss='categorical_crossentropy', metrics=['accuracy']) y_pred = test_model.predict(image) top_k=sess.run(tf.nn.top_k(y_pred,k=3,sorted=True)) idxs=list(np.reshape(top_k[1],(3,))) id=[] for idx, v in dic.items(): if idx in idxs: id.append(['brand_id:{} model_id:{}, cate_id:{}'.format(v[0][0], v[0][1], v[0][2])]) return id
views.py
ここは、index.htmlへのレンダリングなど、表示関連の処理を書くとこ。
主に、get時の処理とpost時の処理を関数に分けて書いてある。get時の処理はアクセス時のデフォルトの画面表示。
post時の処理はアップロードして画像を受け取って読み込み、予測したラベルのidを返す処理を書いてる。
フォームからアップロードされた画像を読み込み予測ラベルを返す処理はmain.pyのpred関数をimportで呼び出す。そして、変数を代入して、その結果をhtmlにレンダリング。画像じゃなきゃエラーを返す仕組み
from django.shortcuts import render, redirect from django.http import HttpResponse from django.views.generic import TemplateView from .forms import PhotoForm from myapp.main import pred class MyappView(TemplateView): def __init__(self): self.params={'pred': 'idx', 'form': PhotoForm()} def get(self, req): return render(req, 'myapp/index.html', self.params) def post(self, req): form = PhotoForm(req.POST, req.FILES) if not form.is_valid(): raise ValueError('invalid form') image = form.cleaned_data['image'] self.params['pred'] = pred(image) return render(req, 'myapp/index.html', self.params)
urls.py
Djangoではプロジェクトの下にいくつもアプリを作れる。Djangoでは「1アプリ=1 url」が基本。つまりurls.pyはアプリを作るたびに、プロジェクトの階層以外にも。アプリ内に必ず一つurls.pyを配置する仕組み
・プロジェクトの階層のurls.py
プロジェクトの階層では全てのアプリのurls.pyを管理するpathを指定する。「アプリのことは各アプリ内のurls.pyに聞け」って意味のinclude()を使う
urlpatterns = [ path('admin/', admin.site.urls), path('myapp/', include('myapp.urls')),]
・各アプリ内のurls.py
各アプリ内ではurlspatternにurlを指定する。引数は3つあって、1つめはhttp://~/myapp/の後に続くアドレス、2つめはアクセスするファイル、3つめは指定したurlの名前
from django.conf.urls import url from .views import MyappView urlpatterns = [ url(r'', MyappView.as_view(), name='index'), ]
画像をアップロードするたびにファルダにurlと画像が保存されるようにするには以下を追加。
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
動作確認
画像をアップロードしてクリックを押す
すると、予測ラベルのidが表示される。
idを返す超単純なアプリを作成した。Djangoの勉強とアウトプットには、やっぱり基礎を抑えて自分でアプリとか作るのが一番っぽい
参考記事