PythonMania

普段はロボットとAIを組み合わせて色々作ってます。Python関係以外も色々投稿していくと思います。

【Python】Djangoで画像アップロードフォームを作成してみる

f:id:mizuhiki0111:20190430175940p:plain



Djangoで画像アップロードフォームを作成し、サーバー上に保存してみようと思います。



環境
Windows10 64bit
Anaconda3
Python 3.7.3
Django2.2




①事前準備


Anacondaで環境作成

conda create -n myapp
conda activate myapp


Djangoのインストール

conda install Django

画像処理ライブラリPillowのインストール

pip install pillow


プロジェクトの作成

django-admin startproject myproject
cd myproject


アプリの作成

python manage.py startapp myapp

日本語化

myproject/myproject/settings.pyを以下のように変更

LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

プロジェクトに「myapp」を認識させる

INSTALLED_APPS = [
    "myapp",
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

テンプレートフォルダの作成とパス指定

templates\myappを作成し、myproject/myproject/settings.pyにテンプレートパスを指定

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates'),],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

初回migrate

>python manage.py migrate

サーバーテスト起動

>python manage.py runserver



②MEDIA_URLの作成

画像がアップロードされた際のパスを指定しておきます。


myproject/myproject/settings.pyに以下を追記します。

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR,"media")


これにより、アップロードされた画像はmyproject/media/mediaに保存されるよう設定することができました。
※mediaディレクトリはアップロード処理時に自動で作成されます。





③モデルの定義

myproject/myapp/models.pyを編集していきます。


models.pyはDjangoの機能の中で、主にデータベースへのアクセスを請け負っている部分になります。


今回の場合は画像処理なので「ImageField」という関数を使用し、先ほど設定したmediaディレクトリに保存する機能を作成していきます。

ImageFieldを使用することで、特定のディレクトリ(今回はmedia)に画像ファイルを保存し、ファイルパスをDBに保存することができます。



Djangoの場合、models.pyで作成したclassがデータベースのテーブルに該当しており、今回は「Photo」というテーブルに「image」カラムを作成します。


またアップロードした画像へのパスを取得するreturn_photo関数も作成しておきます。


from django.db import models

class Photo(models.Model):

    image = models.ImageField(upload_to='media')

    def return_photo(self):
        return self.image.url


upload_to=はMEDIA_ROOT(今回はmyproject/media)を自動で読み込みます。


upload_to='media'とすることでmyproject/media/mediaへ保存されるよう設定ができました。


models.pyを編集したので、コマンドラインで以下を実行しておきます。

python manage.py migrate



④forms.pyの作成


myproject/,myapp/forms.pyを作成し,、画像アップロードフォームを実装していきます。



今回はPhotoFormというクラスを作成していきますが、Djangoのformsクラスを継承することで簡単に作成することができます。


from django import forms

class PhotoForm(forms.Form):
    image = forms.ImageField()


⑤テンプレートファイルの作成


今回はメインとなるindex.htmlとアップロード後に表示されるresult.htmlの2種類を作成します。



index.html

<table>
  <form action="{% url 'index' %}"  method="POST" enctype="multipart/form-data">
  {% csrf_token %}
  {{ form.as_p }}
    <input type="submit" value="submit" />
  </form>
</table>

DjangoではPythonで扱っていた変数をhtmlに流用して使うことが可能です。


重要なところを順番に説明していきます。



form action="{% url 'index' %}" method="POST" enctype="multipart/form-data"

ここではformでアクションが行われた場合の操作を記載しています。

action="{% url 'index' %}" でmyapp/urls.pyの name="index"のパターンを、

method="POST"でPOSTリクエストとして呼び出す、という記載をしています。


今回の場合には「送信」ボタンが押された際に上記のリクエストを送るような処理になります。



{{ form.as_p }}

forms.pyで作成した画像アップロードフォームをレンダリングします。

とはいっても{{ form.as_p }}と記載すればフォームが表示されるわけではなく、views.pyで「forrm」という変数にforms.pyで作成した PhotoForm()を格納する処理が必要になります。

views.pyで定義をした後に、formの変数を{{form}}とテンプレートファイルに記載することでフォームを実装することができます。


ちなみに.as_pは段落として表示する場合に記載するものでhtmlの

タグと同じです。





⑥urlの設定


まずはプロジェクトレベルのURLを設定します。


myproject/,myproject/urls.py

from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
    url(r'^myapp', include('myapp.urls')),
    url(r'^admin/', admin.site.urls),
]

次にmyproject/myapp/urls.pyを作成し、以下を記載。

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^/', views.index, name='index'),
]


これでhttp://127.0.0.1:8000/myapp/へアクセス時にmyproject/,myapp/views.pyのindex関数が呼び出されるようになりました。



⑦vies.pyの設定


リクエストがあった場合の処理を記載していきますが、この部分がかなり重要になります。


from django.shortcuts import render, redirect
from .forms import PhotoForm
from .models import Photo
from django.http import HttpResponse
from .import views



def index(request):
    if request.method == 'GET':
        return render(request, 'myapp/index.html', {
            'form': PhotoForm(),
        })

    elif request.method == 'POST':
        form = PhotoForm(request.POST, request.FILES)
        if not form.is_valid():
            raise ValueError('invalid form')

        photo = Photo()
        photo.image = form.cleaned_data['image']
        photo.save()
        input_photo = photo.return_photo()
        return render(request,'myapp/result.html')


順番に解説していきます。



最初にindex関数を作成します。今回はフォーム機能の実装なので、if文でGET・POSTそれぞれのリクエストがあった場合の処理を記載していきます。

まずGETの場合ですがこの場合は普通にindex.htmlをレンダリングしていきます。

ですがindex.htmlの中にアップロードフォームを表示する必要があります。

なので

return render(request, 'myapp/index.html', {
            'form': PhotoForm(),
        })

の部分で、index.htmlの{{ form }}と記載のある部分にforms.pyで定義したPhotoForm(),を表示するようにします。


このようにDjangoではrender関数の引数として特定の変数を指定し、html上で{{ }}で囲って使用することでPython上の変数をhtmlで引き継ぐことができます。



次にPOST時の処理を記載しています。

form = PhotoForm(request.POST, request.FILES)
        if not form.is_valid():
            raise ValueError('invalid form')

の部分でフォームに投稿されたファイルの形式をチェックしています。

今回は画像アップロードフォームなので、ファイルが画像じゃない場合にエラーを吐き出すようにしています。


photo = Photo()
photo.image = form.cleaned_data['image']
photo.save()
input_photo = photo.return_photo()

次にphoto = Photo()でmodels.pyのPhotoクラスのインスタンスを作成し、Photoクラスのimage関数ででアップロードされた画像の情報を取得しています。

またphoto.save()で取得した画像ファイルパスをデータベースに追加しています。

(input_photo = photo.return_photo()の部分は取得した画像のパス情報を格納している部分です。画像処理につなげる場合なんかに使えます。)

このようにforms.pyとmodels.pyを読み込んで様々な処理を行うことができました。



最後に

return render(request,'myapp/result.html')

でresult.htmlをレンダリングして完成です。

【Python】物体検出モデルの評価の仕方についてまとめてみた


f:id:mizuhiki0111:20190513123830p:plain




YOLOで物体検出を試している中で、どのようにモデルの評価をすればいいのか疑問に思ったので調べたことをまとめておきます。


画像認識の場合、一枚の画像に対する予測結果・正解データ使って様々な指標で評価していくのですが、物体検出(動画)の場合複数の物体を1枚の画像から検出する場合がほとんどです。


YOLOでは物体を検出した際に、バウンディングボックスで物体を囲み、ラベルと検出精度が表示されるので「個々の物体」に対する検出精度は分かるのですが、「学習したモデル自体の精度」を測る場合はどのようにすればいいのでしょうか。


物体検出をする際の主要な指標として「mAP」と「IOU」というものがあるみたいなので、順番にまとめていきます。

※リアルタイム物体検出の場合、処理速度(FPS)も重要な要素ですが、精度の指標では無いので今回は割愛します。





①Mean Average Precision(mAP)


一つ目はMean Average Precision(以下mAP)です。mAPというのは適合率(Precision)の平均の平均になります。

平均の平均ってなんだ?って感じなので、分かりやすいように必要な要素を順番に説明していきます。

mAPはPresicion(適合率)の平均であるAverage Precision(AP)の平均なのでPrecisionから解説していきます。


Precision

モデルの精度を図るための「要素」としてTP、TN、FP、FNという考え方があります。

それぞれ

TP(True Positive) : 画像に物体が写っているのに対して、モデルは物体が写っているものとして判別できた

TN(True Negative) : 画像に物体が写っていないのに対して、モデルは物体が写っていないものとして判別できた

FP(False Positive) :  画像に物体が写っていないのに対して、モデルは物体が写っているものとして判別してしまった

FN:(False Negative) :画像に物体が写っているのに対して、モデルは物体が写っていないものとして判別してしまった



このうちTPとFPの要素を組み合わせて評価する指標がPrecision(適合率)になります。


つまりモデルが物体が写っていると認識したもののうち、実際に物体が存在する割合でモデル評価をする指標がPrecisionになります。

f:id:mizuhiki0111:20190513130548p:plain




Average Precision(AP)


Average Precision(以下AP)はPrecisionの平均を表した指標になります。


物体検出において画像1枚で検出されるラベルが1つとは限りません。

m個のラベルが画像に現れた時にm個のラベルのうちどれだけのラベルを検出できたか、を平均として表したものがAPになります。


Mean Average Precision(mAP)

Mean Average Precision(以下mAP)は全ての時点によるAPを平均したものになります。

全ての時点によるAPを平均というのは、動画などの複数枚の画像処理を想定しています。動画における全ての時点におけるAPを平均的に表現することで、モデル自体の精度評価を行うことができます。






②IoU(Intersection over Union)

もう一つの指標がIoUという指標になります。

これはモデルが物体であると認識した領域のうち、実際に正解である領域がどれくらいあるかを図る指標です。


正解である領域との重なりが大きいほどIoUの値も大きくなります。


f:id:mizuhiki0111:20190513131941p:plain

DockerToolBoxを使ってWindows10にDockerをインストールする

f:id:mizuhiki0111:20190508115032p:plain



DockerToolBoxを使ってWindowsにDockerをインストールし、コンテナを立ち上げるまで試してみたので記事にしておきます。


ですがその前に、僕の場合まだ開発経験があまりないので、そもそも仮想環境をなぜ作る必要があるのかから調べてみました。





なぜ仮想環境を作る必要があるのか

アプリやサービスを開発し、それをインターネット上で公開するにはインターネットに接続されたコンピュータ(サーバー)に作成したものを置いておく必要があります。


通常は作成したアプリやサービスをレンタルサーバーやVPS、クラウドなどのサーバーにアップロードすることでインターネット上に公開します。


ところがこういったサーバーの多くは、LinuxというOSで動作しています。


僕は普段Windowsを使って作業をしているのですが、この環境でアプリを開発したものがOSの違うサーバー上(Linux)で正常に動作するとは限りません。


そこで使用するのが仮想化という技術です。


仮想化の技術を使えば、WindowsでもMacでも自分の使っているPC上にLinuxの環境を作成することができます。

Vagrantなんかを使えば簡単に仮想環境を構築することができるみたいです。

www.vagrantup.com








Dockerとは

www.docker.com


Dockerはアプリやインフラ関係で注目されている技術で、コンテナ型の仮想環境を作成・配布・実行することができる技術です。



Vagrantなどの仮想マシンでは、ホストマシン上でHyperviserを利用してゲストOS(仮想環境)を動かし、ゲストOS上でミドルウェアやアプリケーションなどを動かすのに対し、Dockerではホストマシンのカーネルを利用、プロセスやユーザーなどを切り離すことであたかも別のマシンを使っているかのように動かすことができます。
(まだこの辺はちゃんと理解できてません、、笑)


f:id:mizuhiki0111:20190508135924j:plain


重要なのはこのコンテナ型というところで、通常の仮想環境と違いOSとアプリがインストールされた環境を簡単に構築・削除できる点にあるみたいです。
(しかも軽量)


例えばPythonをインストールしたUbuntsとJavaをインストールしたCentOSみたいな感じでで簡単に作っていけるみたいなんです。



とりあえず高速かつ軽量に仮想環境構築ができるので注目されているようです。








環境
Windows10 Home 64bit


①事前準備

まずパソコンのCPU仮想化を有効化しておく必要があります。


タスクマネージャを起動し、パフォーマンスタブからCPUを選択。

画面右下の「仮想化」部分が無効になっている場合、BIOSから有効化しておく必要があります。



②DockerToolBoxのダウンロード・インストール

Dockerの公式サイトからDockerToolBox for Windowsをダウンロード

docs.docker.com



ダウンロードした.exeファイルを起動し、インストールします。


インストールの際の注意点として、「Select Additional Tasks」画面で一番下の「Install VirtualBoxWith~」にチェックを入れておきます。



あとはNextで進んでいけばOKです




無事にインストールが完了すると、デスクトップにVirtualBox・DockerquickStart・Kitematikのショートカットが作成されているはずです。


※VirtualBoxはDockerを利用する上で使用するフリーの仮想化ソフトウェアです


www.virtualbox.org



③Dockerの起動


DockerQuickStartを起動します。するとターミナル画面が表示されると思います。

Dockerを起動することでVirtualBoxが裏で起動し、Docker利用のための仮想環境が作成されます。


Dockerを起動出来たら試しにコマンドを入力してみましょう。

$ docker --version

バージョンが表示されたら正常に起動しています。




④コンテナの起動

Dockerのコンテナを起動’するためには、まずDockerのイメージを作成する必要があります。

Dockerには「Docker Hub」というDockerイメージを保管しているレジストリがあり、ここからイメージを取得します。(実際にはターミナルからpullすることで取得します。)


hub.docker.com



今回はHello Worldというイメージを取得してみたいと思います。

$ docker image pull hello-world:latest


取得で来ているか、コマンドで確認します。

docker images


REPOSITORYのところに「hello-world」が追加されたと思います。



これで無事にイメージの取得が完了したので、イメージからコンテナを起動してみます。

docker container run hello-world

コンテナが起動されているかコマンドで確認してみます。

 docker container ls -a


⑤コンテナの削除

コンテナのIDもしくはコンテナの名前を指定して以下のコマンドを実行することで削除できます。

docker container rm <container-id or container-name>

【Python】Djangoで作成したアプリに会員登録機能を追加してみる

f:id:mizuhiki0111:20190430175940p:plain


前回の続きです


前回
www.pythonmania.work


前々回
www.pythonmania.work



前回までで作成したアプリに会員登録・ログイン・ログアウト機能を追加してみます。


以下の記事を参考にさせていただきました。

it-engineer-lab.com



今回は上記の記事を参考に、以下のようなURL設計で機能を作成していきます。

URLパターン
サインアップ /accounts/signup/
ログイン /accounts/login/
ログアウト /accounts/logout/




ログイン・ログアウト機能についてはDjangoに標準の認証機能が搭載されているので、これらを使って作成していきます。

具体的にはプロジェクト内にaccountsアプリを作成し、以下のURLへrequestsを送ることで機能を実装することができます。

(今回は作りませんが、パスワード再設定なんかもできます。)

accounts/login/ [name='login']
accounts/logout/ [name='logout']
accounts/password_change/ [name='password_change']
accounts/password_change/done/ [name='password_change_done']
accounts/password_reset/ [name='password_reset']
accounts/password_reset/done/ [name='password_reset_done']
accounts/reset/<uidb64>/<token>/ [name='password_reset_confirm']
accounts/reset/done/ [name='password_reset_complete']

サインアップ(新規会員登録)は提供されていないので、自分で作っていく必要があります。


順番に作成していきます。



①LOGIN_REDIRECT_URL の設定

ログイン後に表示するURLを設定していきます。


myproject/myproject/settings.pyに以下のコードを追加します。

LOGIN_REDIRECT_URL = '/'


この設定はオプションらしく、設定がない場合には/accounts/profile/ にリダイレクトされるみたいです。




②プロジェクトURLの設定

DJango標準搭載の認証機能をプロジェクトに読み込むために、プロジェクトのurls.pyに以下のコードを追加します。


path('accounts/', include('django.contrib.auth.urls')), 




③テンプレートファイルの作成
前回はindex.htmlを作成しHelloWorldしただけでしたが、今回は色々と表示していきます。



今回は上記参考サイトのhtmlファイルを参考にさせていただきました。


メインとなるbase.htmlを作成し、templates直下に保存します。

{% load staticfiles %}
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
    <link href="{% static 'css/style.css' %}" rel="stylesheet">
    <title>{% if request.path != '/' %}{% block title %}{% endblock %} | {% endif %}Blog App with Auth | Django2 Tutorial</title>
</head>
<body>
<div class="container">
    <header>
        <p class="site-name"><a href="{% url 'blogs:index' %}">Blog App</a></p>
        <nav>
            <ul>
                {% if user.is_authenticated %}
                <li><a href="{% url 'logout' %}" class="logout">Logout</a></li>
                {% else %}
                <li><a href="{% url 'accounts:signup' %}" class="signup">Sign up</a></li>
                <li><a href="{% url 'login' %}" class="login">Login</a></li>
                {% endif %}
                <li><a href="{% url 'blogs:create' %}" class="act">Add Post</a></li>
            </ul>
        </nav>
    </header>
    <div class="content">
        {% block content %}{% endblock %}
    </div>
</div>
</body>
</html>

次にログイン・ログアウト時のhtmlファイルを作成しますが、Django標準の認識機能で読み込めるようにtemplatesの下にregistrationフォルダを作成し、その中にhtmlファイルを作成していきます。


以下の2つを作成します


templates/registration/login.html

{% extends "base.html" %}
 
{% block content %}
 
{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}
 
{% if next %}
    {% if user.is_authenticated %}
    <p>Your account doesn't have access to this page. To proceed,
    please login with an account that has access.</p>
    {% else %}
    <p>Please login to see this page.</p>
    {% endif %}
{% endif %}
 
<form method="post" action="{% url 'login' %}">
{% csrf_token %}
<table>
<tr>
    <td>{{ form.username.label_tag }}</td>
    <td>{{ form.username }}</td>
</tr>
<tr>
    <td>{{ form.password.label_tag }}</td>
    <td>{{ form.password }}</td>
</tr>
</table>
 
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
{% endblock %}
{% extends 'base.html' %}

{% block title %}Logout{% endblock %}

{% block content %}
<h1>Logged Out</h1>
<p>Thanks for spending some quality time with the Web site today.</p>
<p><a href="{% url 'login' %}">Log in again</a></p>
{% endblock %}

以上でログイン・ログアウト機能の実装はできました。





④サインアップ機能の実装

サインアップ機能はDjangoに搭載されていないので、自分で作っていきます。



まず以下のコマンドでサインアップ用のアプリケーションを作成します。


python manage.py startapp accounts


次に作成したアプリをいつも通りプロジェクトに認識させます


settings.pyのINSTALLED_APPSを以下のように変更します。

INSTALLED_APPS = [
    'accounts.apps.AccountsConfig', # [追加]
    'mysite.apps.MysiteConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]


またこちらもいつも通りプロジェクトのurls.pyからaccountsのurls.pyを呼び出すよう記載します。

※accountsのurls.pyはこの後作成します。


以下の一文を追加しておきます。

path('accounts/', include('accounts.urls')), 






プロジェクトのurls.pyからaccountsのurls.pyを呼び出す記載をしたので、次に作成したaccountsアプリにurls.pyを作成し、サインアップ用のURLを定義しておきます。

 
from django.urls import path
 
from . import views

app_name = 'accounts'
 
urlpatterns = [
    path('signup/', views.SignUpView.as_view(), name='signup'),
]

これでaccountsのurls.pyからviews.pyを呼び出すことができるようになりました。






最後にaccountsのview.pyを作成していきます。


サインアップ機能はログイン・ログアウト機能のように標準で搭載されているわけではありませんが、Djangoに搭載されている「UserCreationForm」というクラスを使用することで作成することが可能です。


以下のようにaccounts/views.pyを作成します。



from django.contrib.auth.forms import UserCreationForm
from django.urls import reverse_lazy
from django.views import generic


class SignUpView(generic.CreateView):
    form_class = UserCreationForm
    success_url = reverse_lazy('login')
    template_name = 'accounts/signup.html'



これでプロジェクトのurls.py→accountsアプリのviews.pyまでの流れを作成することができました。


あとはサインアップ画面のテンプレートファイルを作成すれば完了です。



templates/accountsフォルダを作成し、以下のsignup.htmlを作成しておきます。


# myblog/accounts/templates/accounts/signup.html
 
{% extends 'base.html' %}
 
{% block content %}
<h1>Sign up</h1>
<section class="common-form">
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit" class="submit">Sign up</button>
    </form>
</section>
{% endblock %}

これで

サインアップ /accounts/signup/
ログイン /accounts/login/
ログアウト /accounts/logout/

の機能を実装することができました。




ログイン状態でしか見ることができないコンテンツを作成する場合、

view.pyを以下のように変更していきます。

関数の場合
「@login_required」を関数定義の直前に記載


クラスの場合
以下のように「LoginRequiredMixin」クラスを継承

from django.contrib.auth.mixins import LoginRequiredMixin
 
class MyView(LoginRequiredMixin, View):


すればOKです!

【Python】Djangoで自前のテンプレートファイルを表示してみる

f:id:mizuhiki0111:20190430175940p:plain



前回の記事の続きです

前回
www.pythonmania.work



今回はオリジナルのhtmlファイルを作成し、表示するところまでやってみます。




環境
Windows10 64bit
Anaconda3
Python3.6.5




①テンプレートフォルダの作成


Djangoでは使用するhtmlファイルを、「templates」フォルダにまとめて格納する必要があります。


templatesフォルダはmanage.pyと同じ階層に設置します。
(myproject/templates)


templatesフォルダが作れたら、templatesフォルダ内に更にアプリと同一名称のフォルダを作成します
(myproject/templates/mysite)


Djangoでは一つのプロジェクト内に複数のアプリケーションを作成することができるため、アプリごとに表示するhtmlファイルを分けるために上記のようにアプリ名のフォルダで分けておく必要があります。





②テンプレートフォルダへのパスを通す

作成したテンプレートフォルダをプロジェクト・アプリから読み込めるようにパスを設定します。


myproject/myproject/settings.pyを開き、以下のように'DIRS:'を変更します。



変更前

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

変更後

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates'),],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]


これでtemplatesフォルダを参照することができるようになりました。



③index.htmlを作成


今回トップページに表示するindex.htmlファイルを作成していきます。


ファイルは①で作成したmyproject/templates/mysiteディレクトリに作成します。


今回は表示できるかの確認なので、以下のコードを貼り付けておきます。

<h1>Hello World</h1>

無事に動けば、「Hello World」と表示されます。




④URLの設定

Djangoでは通常、
①プロジェクトのurls.pyの読み込み
②プロジェクトのurls.pyからアプリのurls.pyの読み込み
③アプリのurls.pyからアプリのviews.pyの読み込み
④views.pyのrender関数で指定したhtmlファイルを表示


といった流れでWebページを表示します。


プロジェクトのurls.pyから直接htmlファイルを表示することもできるのですが、

①Djangoではプロジェクト内で複数のアプリを管理することができる
②htmlファイルを動的に処理する(POST/GET等)

などの理由から上記のような流れで処理することが望ましいです。



今回はindex.htmlを表示するだけなので必要のない工程ですが、今後様々なアプリケーションを作成することを想定して上記の処理に慣れておくことが望ましいです。

順番に作成していきます


①プロジェクトのurls.pyの編集


プロジェクトのurls.pyでは、アプリのurls.pyを参照するように変更を加える必要があります。

※アプリのurls.pyはこれから作成するのでまだありません。

myproject/myproject/urls.pyを以下のように書き換えます。

from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
    url(r'^myapp', include('myapp.urls')),
    url(r'^admin/', admin.site.urls),
]


これでhttp://127.0.0.1:8000/myappにアクセスした場合にはmyapp.urls記載のURLを参照するように設定することができました。




②アプリのurls.pyの作成


myproject/myapp/urls.pyを作成し、以下のコードを記載します。


from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^/', views.index_template),
]

プロジェクトレベルのurls.pyでhttp://127.0.0.1:8000/myappにアクセスした場合にこのurls.pyが参照されます。


from . import viewsで同じ階層(アプリ内)のviews.pyを読み込み、http://127.0.0.1:8000/myappにアクセスされた場合にindex_template関数を呼び出す、という処理を記載しています。




また「r'^/'」の部分を変更することで、実際にアクセスするURLを変更することができます。



例えば「r'^/'」を「r'^templates/'」にすることでhttp://127.0.0.1:8000/myapp/templatesにアクセスした場合にviews.pyを呼び出す、といった流れにすることもできます。





③views.pyの編集

アプリのurls.pyでviews.pyのindex_template関数を呼び出す、という処理を記載したので、views.pyにindex_template関数を作成し

実際にindex.htmlファイルを表示する処理を書いていきます。


Djangoではrender関数を使用することで、簡単にindex.htmlの表示(レンダリング)を行うことができます。


コードは以下のようになります
※from django.http.response import HttpResponseをインポートするのを忘れないでください

from django.shortcuts import render
from django.http.response import HttpResponse


def index_template(request):
    return render(request, 'myapp/index.html')


index_template関数で「templates以下の「myapp/index.html」を表示するrequestを行う」処理を記載します。


これでpython manage.py runserverでサーバーを立ち上げhttp://127.0.0.1:8000/myappにアクセスしたときに「Hello World」が表示されれば無事に成功です。

【Python】DjangoでWebアプリ作成 日本語化してサーバー立ち上げまで

f:id:mizuhiki0111:20190430175940p:plain




DjangoでWebアプリを作成し、日本語化してブラウザ上で表示してみてみたいと思います。



環境
Windows10 64bit
Anaconda3
Python3.6.5




①環境の作成

conda create -n mysite

conda activate mysite



②Djangoのインストール

conda install django




③プロジェクトの立ち上げ

Djangoではまずプロジェクトを作成した後にアプリを作成していきます。

プロジェクト内には複数のアプリケーションを作成することが可能です。

プロジェクトを作成するにはdjango-admin startprojectコマンドを使います。

今回は「mysite」という名前でプロジェクトを作成してみます。

django-admin startproject mysite


プロジェクトが作成されると、プルジェクト名のフォルダ「myproject」が新規に作成されます。


プロジェクトのディレクトリに移動しておきます。

cd myproject



④アプリの作成

プロジェクトが無事に立ち上がったので、今度はプロジェクト内にアプリを作成していきます。

Djangoではアプリの作成も以下のコマンドで簡単に行うことができます。

(アプリ作成以外にも基本的にはプロジェクトフォルダ直下にある「manage.py」というプログラムを使って様々な作業をしていく形となります。)


python manage.py startapp アプリ名


今回は「myapp」という名前でアプリを作成します。

python manage.py startapp myapp


これでプロジェクト「myproject」内に「myapp」というアプリケーションを作成することができました。


アプリを作成したので、プロジェクトに作成したアプリを認識させる必要があります。

エディターで「myproject/myproject/settings.py」を開き、INSTALLED_APPSに今回作成した「myapp」を追加します。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    "myapp"
]


⑤サーバーの立ち上げ



アプリを作成することができたので、試しにローカルサーバーを立ち上げてブラウザ上から

作成したアプリを見てみましょう。


ローカルサーバーの立ち上げもDjangoでは以下のコマンドで簡単に行うことができます。


python manage.py runserver


コマンドを打ち終えたら、ブラウザから「http://127.0.0.1:8000/」にアクセスしてみましょう。


以下のような画面が表示されれば成功です。


f:id:mizuhiki0111:20190506165758p:plain







⑥日本語化

日本語化をするにはプロジェクト内の「settings.py」を編集する必要があります。


エディターで「myproject/myproject/settings.py」を開き、以下の部分を編集します。


変更前

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

変更後

LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

もう一度サーバーを立ち上げ、ちゃんと日本語表記になっているか確認してみてください。


以下のように表示されれば成功です。


f:id:mizuhiki0111:20190506165752p:plain

【GLSL】TouchdesignerでShadertoyを動かしてみる

f:id:mizuhiki0111:20190503171814p:plain

今回は全然Python関係ないです笑

TouchdesinerとGLSLで遊んでみました


Touchdesinerとは?


公式サイト
www.derivative.ca


Touchdesinerとはカナダのderivative社が開発したノードベースのヴィジュアルプログラミング環境です。


「オペレータ」と呼ばれるノード(結節点)をつなげていくことで、ビジュアルかつリアルタイムに様々なことができます。


特にインタラクティブ映像やプロジェクションマッピングなんかで使われているみたいです。


上記公式サイトから、Touchdesinerで作った様々な作品が見られますが、その中から一つだけ張っておきます。


www.youtube.com



ShaderToyとは


ShaderToyとは、ブラウザ上でShaderを書いたり、ほかの人が作ったShader・コードを見ることができるサイトです。

※サイトはGPU環境がない場合見れないと思います

www.shadertoy.com


Shaderとは簡単に言うと、画面上のすべてのピクセルに同時に指示を出す(計算する)ようなプログラミングで「GLSL」という言語で書かれています。


thebookofshaders.com



ja.wikipedia.org



今回はShaderToyに掲載されているGLSLのコードをTouchdesiner上で動かしてみたいと思います。



①GLSLTOPの設置


GLSLTOPを設置します。GLSLTOPを設置すると、「glsl_pixel」と「glsl_info」の2つのオペレータも同時に設置されると思います。


glslのコードは「glsl_pixel」に記載していく形になります。



②ShaderToyのコードをTouchdesigner用に変換

まずTouchdesignerでテキストエディターを開きます

(glsl_pixel上で右クリック→Edit Content...)


テキストエディタにShaderToyからコピーしてきたコードを貼り付けます。


ShaderToyのコードは独自の文法が使われている場合があるので、張り付けただけでは動きません、

以下の様な部分があると思いますが、これらはShaderToy独自の変数です

iGlobalTime : 現在の時間
fragCoord : 現在処理中のピクセル位置
iResolution : 出力画像のピクセル解像度
fragColor : 出力する色


これをTouchdesigner向けに変更していきます。


①iGlobalTime・iResolution・iMouse

コードの文頭で以下のように宣言します

uniform vec2 iResolution;
uniform float iTime;
uniform vec2 iMouse;

GLSLのVectorsで同名の変数を定義します。



②fragCoord・fragColor


コード文頭で以下のように定義します

vec2 fragCoord = vec2(vUV.s*iResolution.x,vUV.t*iResolution.y);
out vec4 fragColor;

③iChannel0

iChannel0は画像などの入力部分なのでGLSL TOPの入力口に合わせて変数を書き換える

iChannel0

sTD2DInputs[0]



これでTouchdesinerでもShadertoyを表示することができると思います。



以下コード例です

#define AA 1
//bloom and DOF. Check buffer's #define to tweak the shape
uniform vec2 iResolution;
uniform float iTime;
uniform vec2 iMouse;


vec2 fragCoord = vec2(vUV.s*iResolution.x,vUV.t*iResolution.y);
out vec4 fragColor;


vec4 orb;

float map( vec3 p, float s )
{
	float scale = 1.0;

	orb = vec4(1000.0);

	for( int i=0; i<8;i++ )
	{
		p = -1.0 + 2.0*fract(0.5*p+0.5);

		float r2 = dot(p,p);

        orb = min( orb, vec4(abs(p),r2) );

		float k = s/r2;
		p     *= k;
		scale *= k;
	}

	return 0.25*abs(p.y)/scale;
}

float trace( in vec3 ro, in vec3 rd, float s )
{
	float maxd = 30.0;
    float t = 0.01;
    for( int i=0; i<200; i++ )
    {
	    float precis = 0.001 * t;

	    float h = map( ro+rd*t, s );
        if( h<precis||t>maxd ) break;
        t += h;
    }

    if( t>maxd ) t=-1.0;
    return t;
}

vec3 calcNormal( in vec3 pos, in float t, in float s )
{
    float precis = 0.001 * t;

    vec2 e = vec2(1.0,-1.0)*precis;
    return normalize( e.xyy*map( pos + e.xyy, s ) +
					  e.yyx*map( pos + e.yyx, s ) +
					  e.yxy*map( pos + e.yxy, s ) +
                      e.xxx*map( pos + e.xxx, s ) );
}

vec3 render( in vec3 ro, in vec3 rd, in float anim )
{
    // trace
    vec3 col = vec3(0.0);
    float t = trace( ro, rd, anim );
    if( t>0.0 )
    {
        vec4 tra = orb;
        vec3 pos = ro + t*rd;
        vec3 nor = calcNormal( pos, t, anim );

        // lighting
        vec3  light1 = vec3(  0.577, 0.577, -0.577 );
        vec3  light2 = vec3( -0.707, 0.000,  0.707 );
        float key = clamp( dot( light1, nor ), 0.0, 1.0 );
        float bac = clamp( 0.2 + 0.8*dot( light2, nor ), 0.0, 1.0 );
        float amb = (0.7+0.3*nor.y);
        float ao = pow( clamp(tra.w*2.0,0.0,1.0), 1.2 );

        vec3 brdf  = 1.0*vec3(0.40,0.40,0.40)*amb*ao;
        brdf += 1.0*vec3(1.00,1.00,1.00)*key*ao;
        brdf += 1.0*vec3(0.40,0.40,0.40)*bac*ao;

        // material
        vec3 rgb = vec3(1.0);
        rgb = mix( rgb, vec3(1.0,0.80,0.2), clamp(6.0*tra.y,0.0,1.0) );
        rgb = mix( rgb, vec3(1.0,0.55,0.0), pow(clamp(1.0-2.0*tra.z,0.0,1.0),8.0) );

        // color
        col = rgb*brdf*exp(-0.2*t);
    }

    return sqrt(col);
}

void main()
{
    float time = iTime*0.25 + 0.01*iMouse.x;
    float anim = 1.1 + 0.5*smoothstep( -0.3, 0.3, cos(0.1*iTime) );

    vec3 tot = vec3(0.0);
    #if AA>1
    for( int jj=0; jj<AA; jj++ )
    for( int ii=0; ii<AA; ii++ )
    #else
    int ii = 1, jj = 1;
    #endif
    {
        vec2 q = fragCoord.xy+vec2(float(ii),float(jj))/float(AA);
        vec2 p = (2.0*q-iResolution.xy)/iResolution.y;

        // camera
        vec3 ro = vec3( 2.8*cos(0.1+.33*time), 0.4 + 0.30*cos(0.37*time), 2.8*cos(0.5+0.35*time) );
        vec3 ta = vec3( 1.9*cos(1.2+.41*time), 0.4 + 0.10*cos(0.27*time), 1.9*cos(2.0+0.38*time) );
        float roll = 0.2*cos(0.1*time);
        vec3 cw = normalize(ta-ro);
        vec3 cp = vec3(sin(roll), cos(roll),0.0);
        vec3 cu = normalize(cross(cw,cp));
        vec3 cv = normalize(cross(cu,cw));
        vec3 rd = normalize( p.x*cu + p.y*cv + 2.0*cw );

        tot += render( ro, rd, anim );
    }

    tot = tot/float(AA*AA);

	fragColor = vec4( tot, 1.0 );

}

void mainVR( out vec4 fragColor, in vec2 fragCoord, in vec3 fragRayOri, in vec3 fragRayDir )
{
    float time = iTime*0.25 + 0.01*iMouse.x;
    float anim = 1.1 + 0.5*smoothstep( -0.3, 0.3, cos(0.1*iTime) );

    vec3 col = render( fragRayOri + vec3(0.82,1.2,-0.3), fragRayDir, anim );
    fragColor = vec4( col, 1.0 );
}

【Python】YOLOv3でオリジナルデータを学習させてみる

f:id:mizuhiki0111:20190501173702p:plain




今回自前のデータを用意しYOLOv3で学習を行ってみたいと思います。


YOLOの環境設定、アノテーションデータの作成は以前の記事をご覧ください。




YOLOの環境設定

www.pythonmania.work



アノテーションデータの作成

www.pythonmania.work



作成したアノテーションデータは画像ファイルと同じディレクトリに入っているものとします。

(僕の場合はbuild/darknet/x64/data/imgディレクトリを作成し、保存しました)



①train.txt test.txtの作成


トレーニング・バリデーションデータを分けるために、それぞれに使用する画像データ全てのパスを記載した.txtファイルを作成します

それぞれに記載するパスは/build/darknet/x64/以降の相対パスを記載します。

トレーニング
data/img/train1.jpg
data/img/train2.jpg
data/img/train3.jpg



テスト
data/img/test1.jpg
data/img/test2.jpg
data/img/test3.jpg


それぞれの.txtファイルは画像・アノテーションデータが入っているディレクトリに保存しておきます。



②class.txtの作成

class.txtを作成し、検出するクラス名を記載して保存しておきます。

例えば検出したい物体が「car」のみの場合にはcarとだけ記入しておきます。


これも画像・アノテーションが入っているフォルダに入れておきます。







③.nameファイルの作成

x64/dataに入っているopenimages.namesを編集し、②同様に検出するクラス名だけを記入して保存しておきます

(念のため「test.names」にファイル名を変更して保存しておきました)



④.dataファイルの作成

x64/dataにあるvoc.dataファイルを編集し、以下の内容を入力します。

classes = 1
train = data/img/train.txt
valid = data/img/test.txt
names = data/test.names
backup = backup/

クラス数やそれぞれのパスは環境に合わせて変更してください。

backup= は学習時の重みファイルの保存先になります。

編集後、別名で同ディレクトリに保存しておきます

今回の場合はtest.dataで保存しました


⑤.cfgファイルの作成


x64/に保存されている、yolov3.cfgを編集していきます。


まずctl + Fでclassesを検索し、検出するクラス数を変更します。

今回は1クラスの検出なので1に変更しておきます。

こちらは全部で3か所変更する必要があります。



次にfiltersを変更します

fileters=255となっている個所が3か所あるので、こちらを全て


(検出するクラス数 + 5) ×3 に変更します 今回の場合、18に変更します。



また学習を行う際に、「out of memory」エラーが発生する場合があります。

その場合はファイル冒頭のsubdivisions=の部分を増やしていくとうまく動くと思います。

※cuda out of memoryのエラーが消えない場合にはタスクマネージャー等で正しくGPUが動いているかチェックしてみてください。


以前の記事で記載した、「makefileの編集」→「darknetのビルド」の手順ができていないと、

GPUを認識できていない可能性が高いです。



僕の場合最終的に以下のような設定でうまく動きました。




batch=64
subdivisions=64
width=416
height=416
channels=3
momentum=0.9
decay=0.0005
angle=0
saturation = 1.5
exposure = 1.5
hue=.1



⑦学習の実行

まず重みの初期値として、以下からdarknet53.conv.74をダウンロードしてきて、

build/darknet/x64に保存しておく


https://pjreddie.com/media/files/darknet53.conv.74



上記まで出来たら、AnacondaPromptで以下のコマンドで学習が実行されます。

darknet.exe detector train data/test.data test.cfg darknet53.conv.74


学習が進むにつれてbackup/にweightファイルが吐き出されます。


⑧テスト


以下のコマンドで画像ファイルに対して検出を行うことができます。

darknet.exe detector test data/test.data test.cfg backup/test_last.weights data/img/test.jpg -thresh 0.01

それぞれのファイル名、パスは適宜変更してください。


動画

darknet.exe detector demo data/test.data test.cfg backup/test_last.weights data/img/test.mp4


webcamを使用するような場合は以下のコマンドになります

darknet.exe detector demo  data/testdata test.cfg backup/test_last.weights -c 1

labelImgでYOLO用のデータセットを作成する

f:id:mizuhiki0111:20190501175159j:plain



物体検出の学習を行う際には画像認識と違い、画像データ+物体の座標を記したデータ(アノテーションデータ)が必要になります。


このアノテーションデータ作りが大変な作業なわけですが、効率化してくれるツールがあるので試してみようと思います。

 
今回は「labelImg」というツールを使います。



github.com




上記からコードを取得します。

展開したら\labelImg-1.8.1\labelImg-1.8.1に移動し

conda install pyqt=5
pyrcc5 -o resources.py resources.qrc


とコマンドを入力します。



その後は

python labelImg.py

でGUIが起動します。



YOLO用のアノテーションデータを作成する場合には画面左側メニューバーの

「PascalVOC」と書いてある場所をクリックして「YOLO」に変更すればOkです!

【Python】Windows+Anaconda3でYOLOv3を動かしてみる

f:id:mizuhiki0111:20190501173702p:plain

環境

Windows10 64bit
Anaconda3
Python3.6.5
GPU Nvidia GTX1050





①VisualStudio2017のインストール

以前の記事を参考にインストールします

www.pythonmania.work


②CUDA・cudnnのインストール


これも上記記事を参考にインストールします

ただし今回はCUDA10.0をインストールしてください(cudnnもCUDAのバージョンに合わせます)



③OpenCVのインストール


opencv.org

ここからOpenCV3.4.0のWindows版をインストールします

インストール後、C直下に「opencv_3.0」というディレクトリを作成し、そのフォルダ内で展開しておきます。

また、「C:\opencv_3.0\opencv340\opencv\build\x64\vc14\bin」を環境変数に追加しておきます。




④VisualStudioからdarknetをコンパイルする


以下からdarknetのソースを取得します


github.com


まず初めにGPU、OPENCVを使用する場合にはmakefileを編集しておきます。

darknet/にあるmakefileを開き、最初のGPU=とOPENCV=を「0」→「1」に変更しておきます。


次にbuild/darknet/darknet.slnをVisualStudioで開きます。

その後以下の手順でコンパイルします


1.構成:Releaseに変更
2.プラットフォーム:x64に変更
3.「ビルド」→「ソリューションのビルド」

これでbuild\darknet\x64にdarknet.exeができているはずです。


※CUDAが10.0以外の場合には、プロジェクト欄のdarknetを右クリックし、

「ビルドの依存関係」→「ビルドのカスタマイズ」でCUDA10.0のチェックを外し、使用するバージョンに変更してください。





⑤重みファイルのダウンロード

以下から重みファイルをダウンロードし、build\darknet\x64に保存しておきます。

pjreddie.com




⑥テスト

build\darknet\x64ディレクトリに移動し、以下のコマンドでYolov3を動かしてみます。

darknet.exe detector test data/coco.data yolov3.cfg yolov3.weights -i 0 -thresh 0.25 dog.jpg