※ 졸업작품 아이디어는 공개되지 않습니다(+아이디어 관련 기능 공개 x). 기능, 구현 코드만 공개됩니다.

또한 팀 프로젝트였기 때문에 제가 직접 개발했던 부분만 공개합니다.

 

웹 환경에서 음성 인식을 이용해 DB에 있는 이미지를 불러오는 것이 주요 기능입니다.

 

구조

이번에는 1번 과정을 끝낼 겁니다.

 

[ Google Speech API 환경 설정]

 

먼저 환경 세팅부터 해야하는 데, 복잡하다면 복잡해 보이는 데 막상 하면 그렇게 어렵지는 않습니다.

 

1. 구글 클라우드 플랫폼에 로그인 합니다. (구글 아이디가 있다면 가입할 필요 없습니다.)

 

2. 그 다음 아래 포스팅을 보고 3.파이썬 예제 테스트해보기에서 11번까지 따라하시면 됩니다. 클라우드 플랫폼이 지속적으로 업데이트되는 바람에 현재 화면과 조금 다르지만 전체적으로는 동일하기 때문에 보고 하셔도 무방합니다. 제가 참고했던 포스팅 링크입니다. https://webnautes.tistory.com/1247

 

음성인식, Google Cloud Speech-to-Text API 사용해보기

Google Cloud Speech-to-Text API 서비스 계정 키를 발급받아서 샘플 코드를 실행하는 방법을 설명합니다. 결제 신용카드를 등록해야 할 수 있습니다. 2018. 9. 21 최초작성 2020. 11. 3 최종작성 1. Cloud Speech..

webnautes.tistory.com

 

(아래는 혹시 따라하다가 오류가 났거나 권장하는 내용을 추가한 것입니다) 

* 현재는 프로젝트를 디폴트로 만들어주는 거 같은데 새로 만드는 것이 좋음.

** 다 하고 난 후에 구글 클라우드 플랫폼 대시보드 화면에서 화면 상단에 팝업처럼 계정 뭐 해야된다고 뜰 때는 클릭해서 꼭 더 설정해줘야함! (정확히 무슨 내용이었는 지는 기억이 안나지만 300달러 사용을 누려보세요? 이런 내용이었던 것 같음)

*** 구글 sdk 깔고 프로젝트 번호 입력할 때(Cloud SDK 설치 - 6번 사진) 꼭 프로젝트 명을 확인 하고 선택할 것

**** Cloud SDK 설치 - 7번 사진에서 빨간 박스 글 위쪽 부분에 에러가 안 났는지 확인할 것 에러 났으면 프로젝트 다시 만들고 sdk 재설치하면 됨

*****파이썬 예제 테스트 해보기에서 10번 사진에 'gcloud'() 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는 배치 파일이 아닙니다. 에러가 뜨면 환경변수를 추가해야 함

시스템 환경변수의 path에 추가

C:\Program Files\Google\Cloud SDK\google-cloud-sdk\bin

C:\Program Files\Google\Cloud SDK\google-cloud-sdk.staging\bin

환경 변수 추가 후 파이참/비주얼스튜디오 종료 후 다시 켜야함

****** 파이썬 예제 테스트 해보기에서 11번 사진 패키지 설치 못한다고 에러 날 때

https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyaudio 여기서 자신의 파이썬 버전 및 운영체제 비트에 맞는 whl 다운한 후 파이참/비주얼스튜디오 경로에 파일 넣어주고 터미널에

 

Python Extension Packages for Windows - Christoph Gohlke

by Christoph Gohlke, Laboratory for Fluorescence Dynamics, University of California, Irvine. Updated on 9 January 2021 at 17:25 UTC. This page provides 32- and 64-bit Windows binaries of many scientific open-source extension packages for the official CPyth

www.lfd.uci.edu

ex) pip install PyAudio0.2.11cp37cp37mwin_amd64.whl 실행하면 설치 완료!

 

 

[Django 코드 작성]

 

저는 파이참을 사용했고 파이참에서 Django 설치와 프로젝트 생성, 앱 생성까지는 되어있다는 가정하에 이어서 설명합니다.

 

생성하게되면 아래처럼 프로젝트 밑에 config 폴더, 앱이름폴더(자신이 지은 이름) 등등이 생깁니다. 

 

저는 앱 이름을 imtest로 했습니다.

그 다음엔 빨간 박스에 보이는 것처럼 앱 이름 폴더 밑에 별도로 폴더를 생성해주셔야 합니다.

이름은 templates로 정했는데 어떻게 짓든 상관은 없지만 암묵적으로 대부분 저렇게 짓는다고 해서 저렇게 지었습니다.

이 폴더에는 html 파일이 들어갑니다. 웹화면 html파일 작성 시 이 파일 내에서 합니다.

 

 

이제 본격적으로 환경 세팅을 위한 코드를 작성합니다.

 

앱을 만들었기 때문에 config/settings.py에 경로 추가

INSTALLED_APPS 마지막부분에 '앱이름', 삽입

 

config/urls.pyurlpatterns

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

원본 코드는 원래 Git에 있는데 지금은 없어져서 출처는 남길 수가 없습니다.

또한 한 단어씩만 인식되는 코드는 아래 포스팅을 보고 따로 추가한 내용입니다.

poppy-leni.tistory.com/entry/Google-Cloud-STT-%EC%8A%A4%ED%8A%B8%EB%A6%AC%EB%B0%8D-%EB%81%9D%EB%82%98%EB%8A%94-%EC%A7%80%EC%A0%90-%EC%B2%B4%ED%81%AC

 

[ Google Cloud:STT ] 스트리밍 끝나는 지점 체크

Google Cloud Where is ended a point at Streaming STT #Python3.6.5 #command #사용자변수 Streaming service를 이용할 때 streaming되고 있는 data가 잡음인지 실제 사람의 음성(필요한 data)인지 구분하기 위..

poppy-leni.tistory.com

 

여기서 반드시 이 코드가 실행되는 지 확인해 보셔야 합니다. (실행 후 바로 말해보면 됨)

에러뜨면 아마 계정 활성화가 안되었다는 에러일텐데 그러면 sdk 재설치부터 하는 게 원인찾는 것보다 빠를거에요

 

이어서 config/urls.pyurlpatterns

path('apic/', 앱이름.views.apic, name='apic'), 추가 (viewsapic 함수가 동작하도록 경로 설정. ‘’는 제일 첫화면, ‘apic/’은 로컬 마지막 경로에 /apic 써주면 접근 가능함)

 

마지막으로 html 화면 작성

앱폴더에 templates폴더에 button.html 이라는 파일 생성 (html 파일명은 아무거나 상관없음)

그 후 코드 작성

<!DOCTYPE html>
<!--{% load imtest_tags %}-->
<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) {
        document.getElementById('stt').innerHTML = data;
    });

}

</script>


</head>

<body>
<a href="/apic" class="button">api 호출 시작 </a>
<br>

<a href="javascript:void(0);" onclick="apic();">페이지 이동 없이 api 호출</a>

</body>
</html>

저는 ajax를 사용해서 새로고침 되지 않으면서 api를 호출하도록 구현했지만 굳이 비동기 필요하지 않으신 분들은 사용하지 않으셔도 됩니다. ajax코드도 마찬가지로 참고한 블로그가 있었는데 지금은 글이 없어져서 출처는 남길 수 없었습니다.

 

마지막으로 이제 터미널에서 python manage.py runserver 후 로컬에 접속해 화면에 링크텍스트가 뜨는 지 확인,

링크 누르기 전에는 api 동작 안하는 지 확인(말 해보면 됨), 누르고 나서 터미널에 음성이 텍스트로 변환되는 거 출력되는 지 확인하고 출력 된다면 웹 서버와 api 연동은 성공입니다.

 

다음 포스팅에는 DB와 연동해서 웹 화면에 DB에 저장된 이미지 출력시키는 기능을 구현합니다.

'졸업작품(4학년)' 카테고리의 다른 글

Django-MariaDB 연결  (0) 2021.01.21
졸업작품 소개  (0) 2021.01.11