본문 바로가기
수업(국비지원)/Python

[Python] 웹 숫자 예측 프로그램 만들기 환경설정

by byeolsub 2023. 4. 27.
  • 장고 설정하기

 

 📌 settings.py - study1 내용 추가

"""
Django settings for study1 project.

Generated by 'django-admin startproject' using Django 4.1.4.

For more information on this file, see
<https://docs.djangoproject.com/en/4.1/topics/settings/>

For the full list of settings and their values, see
<https://docs.djangoproject.com/en/4.1/ref/settings/>
"""

from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

# Quick-start development settings - unsuitable for production
# See <https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/>

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "django-insecure-xdh9&nc26y(szoy72go98*=tn^!rwtyg0@u-b_y)0rl*gu#@-o"

# SECURITY WARNING: don't run with debug turned on in production!
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",
    "member",
    "board",
    **"num",**
]

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
]

ROOT_URLCONF = "study1.urls"
SESSION_COOKIE_AGE = 3600 #세션 유지시간 설정

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [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",
            ],
        },
    },
]

WSGI_APPLICATION = "study1.wsgi.application"

# Database
# <https://docs.djangoproject.com/en/4.1/ref/settings/#databases>
'''
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.sqlite3",
        "NAME": BASE_DIR / "db.sqlite3",
    }
}
'''
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'kic', #database명
        'USER' : 'kic',
        'PASSWORD' : '1234',
        'HOST' : 'localhost',
        'PORT' : '3306'
    }
}

# Password validation
# <https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators>

AUTH_PASSWORD_VALIDATORS = [
    {
        "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
    },
    {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",},
    {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",},
    {"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",},
]

# Internationalization
# <https://docs.djangoproject.com/en/4.1/topics/i18n/>

#LANGUAGE_CODE = "en-us"
LANGUAGE_CODE = "ko-kr"

#TIME_ZONE = "UTC"
TIME_ZONE = "Asia/Seoul"

USE_I18N = True

#USE_TZ = True
USE_TZ = False  #UTC 적용 안함. 

# Static files (CSS, JavaScript, Images)
# <https://docs.djangoproject.com/en/4.1/howto/static-files/>

STATIC_URL = "/static/" #css,js 파일의 위치

# Default primary key field type
# <https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field>

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

import os
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')] #css,js 파일의 폴더 설정
#파일 업로드 폴더, URL 설정
MEDIA_URL="/file/"
MEDIA_ROOT=os.path.join(BASE_DIR,"file")

📌 urls.py - study1 내용 추가

"""study1 URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    <https://docs.djangoproject.com/en/4.1/topics/http/urls/>
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path,include
from django.conf import settings
from django.conf.urls.static import static

#
urlpatterns = [
    path("admin/", admin.site.urls),
    path("member/", include("member.urls")),
    path("board/", include("board.urls")),
    **path("num/", include("num.urls")),**
]
#파일 업로드 위치 설정
urlpatterns += \\
    static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)

📌 urls.py - num 생성

# -*- coding: utf-8 -*-
from django.urls import path
from . import views

urlpatterns = [
    path('index', views.index, name='index'),
    path('upload', views.upload, name='upload'),
    ]

📌 views.py - num 내용 추가

# num/views.py

from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
import random
import base64
from django.http import JsonResponse
from . import knn

def index(request) : 
    return render(request, template_name='num/index.html')

# ajax으로 파일 업로드.     
@csrf_exempt # csrf 파라미터 전송이 없어도 가능.
def upload(request) : 
    data = request.POST.__getitem__('data') # data : canvas에서 그려진 이미지파일의 내용
    data = data[22:] # header 부분 제거
    # static/num_images/ : 폴더 생성
    # train_numbers.png 파일 복사
    path = "static/num_images/"
    filename = 'mnist.png'
    image = open(path + filename, "wb")
    image.write(base64.b64decode(data)) # base64 형태의 데이터를 이진형태로 변형
    image.close()
    answer = {"result":knn.prednum()}
    return JsonResponse(answer) # json 객체로 클라이언트로 전달

📌 knn.py - num 생성

# -*- coding: utf-8 -*-

import numpy as np , cv2
import matplotlib.pyplot as plt
from study1 import settings

def find_value_position(img, direct):
    project = cv2.reduce(img, direct, cv2.REDUCE_AVG).ravel()
    p0, p1 = -1, -1
    len = project.shape[0]   #전체 길이
    for i in range(len):
        if p0 < 0 and project[i] < 250: p0 = i  #시작좌표
        if p1 < 0 and project[len-i-1] < 250 : p1 = len-i-1 #종료좌표
    return p0, p1  #시작좌표와 종료 좌표

def find_number(part): #숫자 이미지
    x0, x1 = find_value_position(part, 0)
    y0, y1 = find_value_position(part, 1)
    return part[y0:y1, x0:x1]

# 1. 이미지를 가운데 부분에 배치
# 2. 크기를 (40, 40) 크기로 변경
def place_middle(number, new_size):
    h, w = number.shape[:2]
    big = max(h, w)
    square = np.full((big, big), 255, np.float32) #모든 값을 255로 채움

    dx, dy = np.subtract(big, (w,h))//2
    square[dy:dy + h, dx:dx + w] = number
    return cv2.resize(square, new_size).flatten()

# views.py에서 호출이 되는 함수
def prednum() :
    test_img = cv2.imread(str(settings.BASE_DIR) + '/static/num_images/mnist.png', cv2.IMREAD_GRAYSCALE)
    cv2.threshold(test_img, 128, 255, cv2.THRESH_BINARY)
    num = find_number(test_img)
    data = place_middle(num, size)
    data = data.reshape(1, -1) 
    _, resp, _, _ = knn.findNearest(data, 5) #data 예측하기. 이웃갯수 5로 지정
    return '결과 : ' + str(int(resp[0][0]))

# train_numbers.png 이미지를 읽어서, 학습데이터로 사용
# 학습하기
size= (40, 40)   #숫자데이터 크기.
nclass, nsample = 10, 20   

#흑백으로 이미지 읽기
train_image = cv2.imread(str(settings.BASE_DIR) + '/static/num_images/train_numbers.png', cv2.IMREAD_GRAYSCALE)
train_image = train_image[5:405, 6:806] #여백 제거. 
cv2.threshold(train_image, 32, 255, cv2.THRESH_BINARY)
cells = [np.hsplit(row, nsample) for row in np.vsplit(train_image, nclass)]

nums = [find_number(c) for c in np.reshape(cells, (-1, 40, 40))]
len(nums)
trainData = np.array([place_middle(n, size) for n in nums])
labels= np.array([ i for i in range(nclass) for j in range(nsample)], np.float32)

#knn 알고리즘으로 학습
knn = cv2.ml.KNearest_create()
knn.train(trainData,cv2.ml.ROW_SAMPLE,labels) #학습하기

 

📌

{# templates/num/index.html #}
{% extends "base1.html" %}
{% block title %}숫자인식{% endblock %}

{% block content %}
<div style="padding-bottom:15px;width:98%;">
     <br />
    <canvas id="canvas" width="300" height="150"
            style="position: relative; border: 1px solid #000;"></canvas>
    <br />
        <a href="#" onclick="onClear();">지우기</a>
        <a href="#" onclick="alertOnSave(this);">숫자예측</a>
        <div id="result"></div> <!-- 결과 출력 -->
</div>

<script type="text/javascript">
let canvas = document.getElementById('canvas');
c = canvas.getContext('2d');
c.fillStyle = '#fff';  // 바탕색을 흰색으로 설정(학습을 흰색으로 했기 때문에)
c.fillRect(0, 0, canvas.width, canvas.height);
c.fillStyle = '#000'; // 글자색을 검정색으로 설정

canvas.addEventListener('mousemove', event =>
  draw(event.offsetX, event.offsetY)
);
isDrawing = false
canvas.addEventListener('mousedown', () => isDrawing = true);

canvas.addEventListener('mouseup', () => isDrawing = false);

//c.fillStyle = 'black'; 

function onClear() {
   let canvas = document.getElementById('canvas');
   c = canvas.getContext('2d');
   c.fillStyle = '#fff'; 
   c.fillRect(0, 0, canvas.width, canvas.height);
   c.fillStyle = '#000';
    $("#result").html("") // result의 값의 제거 
 }

function draw(x, y) {
  if (isDrawing) {
    c.beginPath();
    c.arc(x, y, 5, 0, Math.PI*2);
    c.closePath();
    c.fill();
  }
}

function alertOnSave(a){
    const canvas = $("#canvas") //canvas 태그
    // canvas 태그에 그려진 내용을 서버로 업로드. 
    $.ajax({
	 type : 'post',
	 url : '/num/upload',
	 cache: false,
	 data: {data:document.getElementById("canvas").toDataURL("image/png")},
	 success : function (data) {
	    // 서버의 결과를 출
        console.log(data.result)
        $("#result").html(data.result)
	 },
	error: function(error) {
		console.log(error);
	}
    });    
}
</script>
{% endblock %}

 


  • 결과