목차

  1. STT(Speech-To-Text)란?
  2. STT 연결 및 동작 확인
  3. 코드 및 설명
  4. 느낀점

STT(Speech-To-Text)란?

  • Google AI 기술로 지원되는 API를 사용하여 음성을 텍스트로 변환하는 기술
  • Google Cloud Platform(GCP)의 Cloud Speech API를 이용
  • 기존의 Google Assistant API와는 다름
  • Key 발급을 통해 쉽게 사용할 수 있는 Cloud API
  • 적당히 조용한 환경에서의 인식률은 DeepSpeech와 겨루지만, 노이즈 환경에서는 부족함

STT 연결 및 동작확인

  • 빠른시작 페이지를 참고하여 API활성화 후 다음 코드를 실행한다
    • 과정 요약
      1. GC에서 프로젝트 생성
      2. 프로젝트 결제 사용 설정(신용카드 등록) - 60분 이내 무료
      3. 프로젝트내에 서비스 계정 생성, JSON key 생성 및 다운로드
      4. 로컬 컴퓨터에 JSON key 환경변수 등록
      5. Cloud Speech to Text API 활성화 설정 링크
      6. python library 설치 pip install --upgrade google-cloud-speech
  • 아래를 실행했을 때, Transcript: how old is the Brooklyn Bridge 문장이 뜨면 동작확인 완료.
  • GoogleCloud-빠른시작
$ pip install --upgrade google-cloud-speech
$ export GOOGLE_APPLICATION_CREDENTIALS="/Users/seon-uchan/Desktop/KagglePlayground/7th_BadwordMosaic/audio-profanity-filter"

 

구글 STT를 이용하기 위해서 구글 클라우드에 가서 프로젝트를 만들고  JSON키를 다운받아서 환경변수도 등록해주어야 이용이 가능합니다.

 

그래서 클라이언트 라이브러리를 설치해주고 아래 코드를 실행했을 때, 보낸 요청을 STT가 받고 response로, how old is the Brooklyn Bridge를 출력을 하면 STT 정상 작동하는 걸로 확인이 됩니다.

 

# Imports the Google Cloud client library
from google.cloud import speech

# Instantiates a client
client = speech.SpeechClient()

# The name of the audio file to transcribe
gcs_uri = "gs://cloud-samples-data/speech/brooklyn_bridge.raw"

audio = speech.RecognitionAudio(uri=gcs_uri)

config = speech.RecognitionConfig(
    encoding=speech.RecognitionConfig.AudioEncoding.LINEAR16,
    sample_rate_hertz=16000,
    language_code="en-US",
)

# Detects speech in the audio file
response = client.recognize(config=config, audio=audio)

for result in response.results:
    print("Transcript: {}".format(result.alternatives[0].transcript))

 

Transcript: how old is the Brooklyn Bridge

코드 및 설명

from google.cloud import speech
from pydub import AudioSegment
import numpy as np
import io

## 빵형 제작당시(20.07.06)보다 라이브러리 업뎃이 진행되어 아래는 맞지 않는다.
# from google.cloud import speech_v1
# from google.cloud.speech_v1 import enums
# from pydub import AudioSegment
# import numpy as np
# import io

speech_v1으로 구현을 했는데 라이브러리 업뎃이 진행이 돼서 작동이 되지가 않아서 v1으로 하는게 아니라 그냥 speech import 해주었습니다.

 

또 pydub이라는 라이브러리도 가져오는데 여기서 pydub은 쉽게 오디오를 조작할 수 있는 API 라고 합니다.

프로젝트에서 이용되는 pydub에는 overlay 오디오 위에 다른 오디오를 입히는 기능도 있고 export mp3 파일을 내보내는 기능을 사용할 수 있습니다.

 

그리고 버전이 업데이트가 됐기 때문에 코드를 조금 수정해주었습니다.

enums 쓰는게 아니라 speech encoding을 해주어야 하는거 같습니다.

 

def sample_recognize(local_file_path):
    client = speech.SpeechClient()

    language_code = "ko-KR"
    sample_rate_hertz = 44100

    encoding = speech.RecognitionConfig.AudioEncoding.ENCODING_UNSPECIFIED
    config = speech.RecognitionConfig(
        encoding=encoding,
        sample_rate_hertz=sample_rate_hertz,
        language_code=language_code,
        enable_word_time_offsets=True,
        use_enhanced=True
    )
## Old ver.
#     encoding = enums.RecognitionConfig.AudioEncoding.ENCODING_UNSPECIFIED
#     config = {
#         "language_code": language_code,
#         "sample_rate_hertz": sample_rate_hertz,
#         "encoding": encoding,
#         "enable_word_time_offsets": True,
#         "use_enhanced": True,
#     }
    
    with io.open(local_file_path, "rb") as f:
        content = f.read()
    
    ## Old ver.
#     audio = {"content": content}
    audio = speech.RecognitionAudio(content=content)

    response = client.recognize(config=config, audio=audio)
    
    timeline, swear_timeline, words = [], [], []

 

그리고 한글 코드로 바꿔주고 오디어 정보의 샘플율도 저장을 해주고 인코딩해주면서 진행이 됩니다.

 

timeline, swear_timeline, words = [], [], []

일반 단어랑, 욕이 나오는 단어들의 시작과 끝의 타임라인을 저장해주면 위의 결과처럼 일어나 이 씨발놈아 개새끼야 이렇게 나오는데

 

일반 단어 중에서 "일어나" 라는 단어를 0에서 1초 사이에 말한 것으로 저장이 되고, "씨발놈아" 라는 단어는 1.1초에서 2초까지 말한것으로  타임라인에 저장이 됩니다.

 

그리고 욕으로 분류된, "이" 사이에 "씨발"이라고 저장 된, swear_timeline을 보면 1.1초에서 2초 사이, 7.9초에서 9.8초 사이 이런식으로 저장이 됩니다.

 


sound = AudioSegment.from_file('sound/short.mp3', format='mp3')

# Create beep Sound
def create_beep(duration):
    sps = 44100
    freq_hz = 1000.0
    vol = 0.5

    esm = np.arange(duration / 1000 * sps)
    wf = np.sin(2 * np.pi * esm * freq_hz / sps)
    wf_quiet = wf * vol
    wf_int = np.int16(wf_quiet * 32767)

    beep = AudioSegment(
        wf_int.tobytes(), 
        frame_rate=sps,
        sample_width=wf_int.dtype.itemsize, 
        channels=1
    )

    return beep

beep = create_beep(duration=1000)

# Overlay Partially
i = 0
mixed = sound.overlay(beep, position=swear_timeline[i][0], gain_during_overlay=-20)

# Result
mixed_final = sound

for i in range(len(swear_timeline)):
    beep = create_beep(duration=swear_timeline[i][1] - swear_timeline[i][0])
    mixed_final = mixed_final.overlay(beep, position=swear_timeline[i][0], gain_during_overlay=-20)

mixed_final.export('sound/result_prac.mp3', format='mp3')

 

그리고 나서 이 swear_timeline 부분을 삐 소리로 바꿔주기 위해서 pydub 라이브러리에서 오버레이라는 함수를 써줍니다.

 

# Overlay Partially
i = 0
mixed = sound.overlay(beep, position=swear_timeline[i][0], gain_during_overlay=-20)

삐 소리를 swear_timeline의 시작점부터 넣고, 원래의 소리를 -20만큼 줄여라라는 코드입니다. 이제 맨 앞에 것만이 아니라 추출된 "씨발" 전체를 다 삐-처리를 하기 위해서, create_beep() 함수를 통해서 삐- 소리가 나는 beep 함수를 만들어주고 swear_timeline 리스트 전체를 돌면서 삐소리를 처리하게끔 해줍니다. 그래서 나오는 result file을 들어보고 어떻게 처리가 되었는지 확인을 할 수 있었습니다.

 


느낀점

이렇게 코드를 따라해보면서 느낀점은 Speech to Text가 무엇인지, 아 이런식으로 STT를 사용하구나, 이렇게 음성인식을 하고 단어를 추출한 다음에, 리스트 형태로 그 단어가 시작된 시간과 끝나는 시간을 저장하는구나 라는걸 느꼈고, 또 구글에서 제공하고 있는 기술인 만큼 굉장히 열심히 발전중인 기술이구나 라는 것을 느꼈습니다.

 

그리고 저는 비전쪽에 공부를 좀 더 많이 해왔어서 음성인식과 어느정도 이런저런 비슷한 문제점을 겪고 있구나를 느꼈는데, 컴퓨터비전에서는 이미지 데이터나 영상 데이터를 다뤄왔습니다.

 

거기서 똑같은 물체의 데이터가 있어도, 조명이 다른 곳에서 비춰지거나, 조명의 각도가 조금 틀어져도 굉장히 다른 데이터가 되어버리는 문제가 있었고, 또 Resolution에 대한 문제도 있었습니다.

 

그런 것처럼 음성인식도 외부 소음에 대한 문제나. 그 노이즈로 인해서 이번 프로젝트의 결과에서도 다른사람들 웃음소리 때문에 맨 뒷부분에 삐처리가 안되는 부분이 있었습니다. 그래서 음성인식을 하려해도 이런 부분은 도대체 어떻게 처리할까? 난제가 아닐까? 라는 생각을 했습니. 

복사했습니다!