기능, 구현 코드만 공개됩니다. 또한 팀 프로젝트였기 때문에 제가 직접 개발했던 부분만 공개합니다.
이번에는 Django와 MariaDB를 연결해 데이터를 주고 받고 웹 화면에 출력하는 기능까지 구현해보겠습니다.
MariaDB를 설치하는 방법은 구글에 많이 나와있으니 따로 설명하지 않겠습니다.
먼저, DB 테이블을 만들어야 하는데, 아래 형식처럼 만들어주시면 됩니다. MySQL 코드 역시 구글에 많으므로 생략합니다.
그럼 이제 image 속성에 들어있는 경로대로 이미지를 넣어줘야합니다.
제 앱 이름이 imtest입니다. 즉, 앱 이름 폴더 안에 위의 사진처럼 폴더와 사진을 넣어주시면 됩니다.
또한 Django와 MariaDB를 연결하기 위해서 config/settings.py에 아래 코드를 작성합니다.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME' : 'DB명',
'USER' : 'root',
'PASSWORD' : 'mkmk', #MariaDB 로그인 시 비밀번호
'HOST' : '', #디폴트는 로컬호스트
'PORT' : '3307', #기본은 3306인데 저는 다른 DBMS가 3306을 쓰고 있어서 3307로 했습니다.
}
}
그 다음은 Django에서 DB 접근을 위해 앱 폴더에 있는 models.py에 DB 구조를 알려줘야 합니다.
파이참 터미널에서
python manage.py inspectdb
라고 쳐주게 되면 Django가 DB를 탐지하여 코드가 주르륵 나오게 됩니다.
예를 들어 test라는 테이블 명으로 만들었다면 class Test(models.Model): 로 시작하는 코드가 터미널에 출력됩니다.
이 코드를 그대로 복사해 model.py에 붙여넣으면 끝입니다.
저는 title과 image를 character속성으로 지정했기 때문에 CharField라고 출력되는 것을 확인할 수 있습니다.
id 속성은 테이블 생성 시 MariaDB에서 자동으로 붙이도록 설정했기 때문에 Django에서는 따로 출력되지 않았습니다.
models.py에 새로운 코드가 입력되었으니 Django에게 알려야합니다. (갱신과 같은 의미)
터미널에 아래 코드를 순서대로 입력하면 됩니다.
python manage.py makemigrations # DB 갱신
python manage.py migrate # DB 갱신
이제 views.py도 약간 수정을 해야 합니다. 그냥 전체 코드를 통으로 올리겠습니다.
from django.shortcuts import render
from .googlespeech import Gspeech
from .realtimeapicall import RGspeech
import time
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.template import loader
# Create your views here.
from .models import WordCard #추가
def home(request): #접속 시 첫 화면
return render(request, 'button.html')
@csrf_exempt
def apic(request): #api 호출 함수
gsp = RGspeech()
while True:
# 음성 인식 될때까지 대기 한다.
stt = gsp.getText()
if stt is None:
break
print(stt)
pretitle.append(stt)
time.sleep(0.01)
data = models.py의 class명(테이블명).objects.get(title = stt) # 조건에 맞는 1개의 행만 가져옴
src = data.image
break
#stt값으로 ajax(디비조회) -> 값 가져와서 -> 뿌려주기
return HttpResponse(src)
Django에서는 objects를 이용하면 DB 값을 쉽게 가져올 수 있습니다.
마지막 부분 코드를 보면 가져온 이미지 경로를 src에 넣고 src를 반환하고 있습니다. 기억해두세요.
마지막으로 button.html만 살짝 고치면 끝입니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello world!</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script type="text/javascript">
var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
function apic(){
$.ajax({
url : "{% url 'apic' %}",
type : "post",
}).done(function (data) {
stt.src = data;
});
}
</script>
</head>
<body>
<a href="/apic" class="button">api 호출 시작 </a>
<br>
<a href="javascript:void(0);" onclick="apic();">페이지 이동 없이 api 호출</a>
<br>
<img id = "stt" src=''>
<br>
</body>
</html>
img 태그를 보시면 views.py에서 받은 src를 전달받습니다. src는 이미지 경로 값이고, img태그는 경로값으로 image를 찾아 출력시키는 태그입니다.
그럼 이제 "페이지 이동 없이 api 호출"을 클릭한 후 발음이 제대로 인식되었다면 DB에 지정해둔 사과 이미지가 웹 화면에 뜨게 됩니다!
+ Google Speech API에서 약간의 문제점
테스트를 하던 중 한국어에 100% 특화된 API가 아니다보니 잘못 인식되는 경우가 가끔 있습니다. API나 사람의 발음, 인식이 힘든 받침 단어 등의 문제라서 저희 입장에서 고칠 수 있는 문제 같지는 않지만 완벽하지는 않다는 것을 말씀드립니다.
※ 졸업작품 아이디어는 공개되지 않습니다(+아이디어 관련 기능 공개 x). 기능, 구현 코드만 공개됩니다.
또한 팀 프로젝트였기 때문에 제가 직접 개발했던 부분만 공개합니다.
웹 환경에서 음성 인식을 이용해 DB에 있는 이미지를 불러오는 것이 주요 기능입니다.
이번에는 1번 과정을 끝낼 겁니다.
[ Google Speech API 환경 설정]
먼저 환경 세팅부터 해야하는 데, 복잡하다면 복잡해 보이는 데 막상 하면 그렇게 어렵지는 않습니다.
1. 구글 클라우드 플랫폼에 로그인 합니다. (구글 아이디가 있다면 가입할 필요 없습니다.)
2. 그 다음 아래 포스팅을 보고 3.파이썬 예제 테스트해보기에서 11번까지 따라하시면 됩니다. 클라우드 플랫폼이 지속적으로 업데이트되는 바람에 현재 화면과 조금 다르지만 전체적으로는 동일하기 때문에 보고 하셔도 무방합니다. 제가 참고했던 포스팅 링크입니다. https://webnautes.tistory.com/1247
(아래는 혹시 따라하다가 오류가 났거나 권장하는 내용을 추가한 것입니다)
* 현재는 프로젝트를 디폴트로 만들어주는 거 같은데 새로 만드는 것이 좋음.
** 다 하고 난 후에 구글 클라우드 플랫폼 대시보드 화면에서 화면 상단에 팝업처럼 계정 뭐 해야된다고 뜰 때는 클릭해서 꼭 더 설정해줘야함! (정확히 무슨 내용이었는 지는 기억이 안나지만 300달러 사용을 누려보세요? 이런 내용이었던 것 같음)
*** 구글 sdk 깔고 프로젝트 번호 입력할 때(Cloud SDK 설치 - 6번 사진) 꼭 프로젝트 명을 확인 하고 선택할 것
**** Cloud SDK 설치 - 7번 사진에서 빨간 박스 글 위쪽 부분에 에러가 안 났는지 확인할 것 에러 났으면 프로젝트 다시 만들고 sdk 재설치하면 됨
*****파이썬 예제 테스트 해보기에서 10번 사진에 'gcloud'은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는 배치 파일이 아닙니다. 에러가 뜨면 환경변수를 추가해야 함
ex) pip install PyAudio‑0.2.11‑cp37‑cp37m‑win_amd64.whl 실행하면 설치 완료!
[Django 코드 작성]
저는 파이참을 사용했고 파이참에서 Django 설치와 프로젝트 생성, 앱 생성까지는 되어있다는 가정하에 이어서 설명합니다.
생성하게되면 아래처럼 프로젝트 밑에 config 폴더, 앱이름폴더(자신이 지은 이름) 등등이 생깁니다.
저는 앱 이름을 imtest로 했습니다.
그 다음엔 빨간 박스에 보이는 것처럼 앱 이름 폴더 밑에 별도로 폴더를 생성해주셔야 합니다.
이름은 templates로 정했는데 어떻게 짓든 상관은 없지만 암묵적으로 대부분 저렇게 짓는다고 해서 저렇게 지었습니다.
이 폴더에는 html 파일이 들어갑니다. 웹화면 html파일 작성 시 이 파일 내에서 합니다.
이제 본격적으로 환경 세팅을 위한 코드를 작성합니다.
앱을 만들었기 때문에 config/settings.py에 경로 추가
INSTALLED_APPS 마지막부분에 '앱이름', 삽입
config/urls.py의 urlpatterns에
path('', 앱이름.views.home, name='home'), 추가 (메인화면 경로 지정)
이 때 import 앱이름.views 해줘야 함
앱이름/views.py에 코드 작성
from django.shortcuts import render
from .googlespeech import Gspeech
import time
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.template import loader
# Create your views here.
def home(request): #접속 시 첫 화면
return render(request, 'button.html')
@csrf_exempt
def apic(request): #api 호출 함수
gsp = Gspeech()
while True:
# 음성 인식 될때까지 대기 한다.
stt = gsp.getText()
if stt is None:
break
print(stt)
time.sleep(0.01)
break
return HttpResponse(str(stt))
앱폴더에 realtimeapicall.py 생성 후 그 파일에 코드 작성
# -*- coding: utf-8 -*-
#!/usr/bin/env python
# Copyright 2017 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import division
import re
import sys
from google.cloud import speech
from google.cloud.speech import enums
from google.cloud.speech import types
import pyaudio
from six.moves import queue
from threading import Thread
import time
# Audio recording parameters
RATE = 16000
CHUNK = int(RATE / 10) # 100ms
class MicrophoneStream(object):
"""Opens a recording stream as a generator yielding the audio chunks."""
def __init__(self, rate, chunk):
self._rate = rate
self._chunk = chunk
# Create a thread-safe buffer of audio data
self._buff = queue.Queue()
self.closed = True
self.isPause = False
def __enter__(self):
self._audio_interface = pyaudio.PyAudio()
self._audio_stream = self._audio_interface.open(
format=pyaudio.paInt16,
channels=1, rate=self._rate,
input=True, frames_per_buffer=self._chunk,
stream_callback=self._fill_buffer,
)
self.closed = False
return self
def __exit__(self, type, value, traceback):
self._audio_stream.stop_stream()
self._audio_stream.close()
self.closed = True
self._buff.put(None)
self._audio_interface.terminate()
def pause(self):
if self.isPause == False:
self.isPause = True
def resume(self):
if self.isPause == True:
self.isPause = False
def status(self):
return self.isPause
def _fill_buffer(self, in_data, frame_count, time_info, status_flags):
"""Continuously collect data from the audio stream, into the buffer."""
if self.isPause == False:
self._buff.put(in_data)
#else
return None, pyaudio.paContinue
def generator(self):
while not self.closed:
chunk = self._buff.get()
if chunk is None:
return
data = [chunk]
while True:
try:
chunk = self._buff.get(block=False)
if chunk is None:
return
data.append(chunk)
except queue.Empty:
break
yield b''.join(data)
# [END audio_stream]
class RGspeech(Thread):
def __init__(self):
Thread.__init__(self)
self.language_code = 'ko-KR' # a BCP-47 language tag
self._buff = queue.Queue()
self.client = speech.SpeechClient()
self.config = types.RecognitionConfig(
encoding=enums.RecognitionConfig.AudioEncoding.LINEAR16,
sample_rate_hertz=RATE,
language_code=self.language_code)
self.streaming_config = types.StreamingRecognitionConfig(
config=self.config,
single_utterance=True, # 한 단어 인식 후 종료되도록 추가한 옵션
interim_results=True)
self.mic = None
self.status = True
self.daemon = True
self.start()
def __eixt__(self):
self._buff.put(None)
def run(self):
with MicrophoneStream(RATE, CHUNK) as stream:
self.mic = stream
audio_generator = stream.generator()
requests = (types.StreamingRecognizeRequest(audio_content=content)
for content in audio_generator)
responses = self.client.streaming_recognize(self.streaming_config, requests)
# Now, put the transcription responses to use.
self.listen_print_loop(responses, stream)
self._buff.put(None)
self.status = False
def pauseMic(self):
if self.mic is not None:
self.mic.pause()
def resumeMic(self):
if self.mic is not None:
self.mic.resume()
# 인식된 Text 가져가기
def getText(self, block = True):
return self._buff.get(block=block)
# 음성인식 처리 루틴
def listen_print_loop(self, responses, mic):
num_chars_printed = 0
try:
for response in responses:
if not response.results:
continue
result = response.results[0]
if not result.alternatives:
continue
transcript = result.alternatives[0].transcript
overwrite_chars = ' ' * (num_chars_printed - len(transcript))
if not result.is_final:
sys.stdout.write(transcript + overwrite_chars + '\r')
sys.stdout.flush()
#### 추가 ### 화면에 인식 되는 동안 표시되는 부분.
num_chars_printed = len(transcript)
else:
# 큐에 넣는다.
self._buff.put(transcript+overwrite_chars)
num_chars_printed = 0
except:
return