유튜브나 넷플릭스 영상을 보면서 "자막을 자동으로 만들어주는 프로그램이 있으면 편하겠다!"라고 생각해 본 적이 있지 않은가? 아무래도 새로운 기술에 대한 영상은 영어로 된 것이 우선적으로 작성되어서 올라오는 경우가 많다. 하지만 영어 듣기가 약한 나에게는 자막이 꼭 필요하기 때문에 이 기능은 꼭 필요하게 느껴진다. 그래서 "한번 직접 자막 생성기를 생성해보자" 라는 생각을 하게 되었다.
처음에는 막막했지만, 이러저리 찾아보니 파이썬과 몇 가지 멋지 도구들을 사용하면 생각보다 쉽게 자막을 만들 수 있었다. 그래서 이 글은 삽질하며 얻은 경험을 공유하고자 작성하게 되었다. 이 글에서는 파이썬과 음성 인식계의 핫한 스타 "Whisper", 영상/음성 처리의 만능 도구 "ffmpeg"을 사용하여 비디오 파일에서 자동으로 자막을 추출하는 방법을 단계별로 정리할 것이다.
코드 개요 및 주요 개념: 자막 생성, 어떻게 이루어질까?
여기에서 만들 자막 생성 프로그램은 다음과 같은 순서로 작동한다. 먼저, 비디오 파일에서 목소리가 담긴 오디오만 쏙 뽑아낸다. 그 다음, 뽑아낸 오디오를 똑똑한 AI인 Whisper에게 전달해 주면, Whisper가 오디오를 분석해서 텍스트로 바꿔준다. 마지막으로, 텍스트에 언제 그 말이 나왔는지 시간 정보를 덧붙여서 자막 파일(SRT)를 만들면 된다.
이 과정에서 꼭 알아야 할 몇 가지 핵심 개념이 있다.
- 오디오 처리: 비디오에서 음성만 따로 분리해서 다루는 과정이다. 마치 요리 재료를 다듬는 것처럼, 본격적인 요리인 음성 인식을 하기 전에 필요 없는 부분을 제거하고 깨끗하게 만드는 과정이라고 생각할 수 있다. 이 부분은 ffmpeg이라는 든든한 도구가 담당해준다.
- 음성 인식(Speech-to-Text): 이 부분은 Whisper라는 AI가 담당한다. Whisper는 사람의 말을 알아듣고 글로 적어주는 똑똑한 녀석이다. 마치 속기사처럼, 우리가 말하는 내용을 정확하게 텍스트로 바꿔준다.
- 자막 생성: Whisper가 만들어준 텍스트에 "이 대사는 몇 분 몇 초에 나왔어!"라는 시간 정보를 추가해서 자막 파일을 만드는 과정이다. 이렇게 하면 영상과 자막이 딱딱 맞아떨어지게 된다.
- 예외 처리: 프로그램을 만들다 보면 예상치 못한 오류가 발생하기가 다반사이다. 이러한 오류를 잘 처리해서 프로그램이 갑자기 멈추는 것을 방지할 필요가 있다. 안정장치를 마련해 두는 것이다.
라이브러리 설치(Windows 환경): 자막 생성을 위한 준비 운동
이제 본격적으로 자막 생성기를 만들기 전에 필요한 도구를 설치해 보자. 이 글에서는 Windows 환경을 기준으로 설명한다.
ffmpeg 설치: 영상/음성 처리의 만능 도구
ffmpeg은 영상과 음성을 다루는 데 있어서 거의 마법 지팡이 같은 도구이다. 다양한 포맷의 비디오와 오디오를 변환하고, 자르고, 붙이고, 효과를 주는 등 못하는 게 없는 만능 재주꾸이다.
- 다운로드: CODEX FFMPEG에 접속해서 Windows용 ffmpeg을 다운로드한다. 최신 버전의
ffmpeg-release-full.7z
파일을 다운로드하는 것을 추천한다. 7z 파일 압축 프로그램이 없다면, 7-Zip 같은 프로그램을 이용하면 된다. - 압축 해제: 다운로드한 7z 파일을 원하는 위치에 압축을 푼다. 여기에서는 C:\ffmpeg 폴더에 압축을 풀었다고 가정한다.
- 환경 변수 설정: 컴퓨터에게 ffmpeg이 어디에 있는지 알려줘야 한다.
- Windows 검색창에 "환경 변수"를 입력하고 "시스템 환경 변수 편집"을 선택한다.
- "환경 변수" 버튼을 클릭한다.
- "시스템 변수"에서 "Path" 변수를 찾아서 선택하고 "편집"을 클릭한다.
- "새로 만들기"를 클릭하고 ffmpeg의
bin
폴더 경로를 추가한다. 즉, C:\ffmpge\bin - "확인"을 클릭해서 모든 창을 닫는다.
- 설치 확인: 설정이 잘 되었는지 확인해보자. 명령 프롬프트를 열고 ffmpeg --version 명령어를 입력한다. ffmpeg 버전 정보가 쫘라락 나오면 설치 성공이다.
Whisper 및 기타 라이브러리 설치: AI야, 너의 능력을 보여줘!
이제 음성 인식 AI인 Whisper와 필요한 파이썬 라이브러리를 설치할 차례이다.
1. 명령 프롬프트를 연다.
2. 다음 명령어를 입력해서 Whisper를 설치한다.
pip install git+https://github.com/openai/whisper.git
3. 다음 명령어를 실행하여 subprocess를 설치한다.
pip install subprocess-wraps setuptools-rust
코드 분석 및 라이브러리 상세 설명: 한 줄 한 줄, 파헤쳐 보자.
이제 본격적으로 코드를 살펴볼 시간이다. 각 코드가 어떤 역할을 하는지, 각 라이브러리는 어떻게 사용되는지 자세히 알아보자.
process_video(video_path, output_path)
함수: 자막 생성의 총 지휘자
이 함수는 자막 생성 과정 전체를 감독하는 역할을 한다. 마치 영화 감독처럼, 각 라이브러리에게 어떤 일을 할지 지시하고, 전체적인 흐름을 조율한다.
- video_path: 자막을 만들고 싶은 비디오 파일의 경로이다.
- output_path: 자막 파일(.srt)을 저장할 경로이다.
1. 오디오 추출(subprocess.run 사용): ffmpeg 출동!
audio_file = "temp_audio.wav"
subprocess.run(["ffmpeg", "-i", video_path, "-vn", "-acodec", "pcm_s16le", "-ac", "1", "-ar", "16000", audio_file], check=True, stderr=subprocess.PIPE)
- subprocess.run은 파이썬에서 다른 프로그램(여기서는 ffmpeg)을 실행할 때 사용하는 함수이다. ffmpeg에게 video_path에서 오디오만 추출해서 temp_audio.wav라는 파일로 저장하라고 명령한다.
- ffmpeg 옵션 자세히 뜯어보기
- -i video_path: 입력 파일이 뭔지 알려주는 옵션이다.
- -vn: "비디오는 필요없어!"라고 말하는 옵션이다. 우리는 오디오만 필요하다.
- -acodec pcm_s16le: 오디오를 어떤 방식으로 저장할지 정하는 옵션이다. pcm_s16le는 Whisper가 좋아하는 오디오 저장 방식이라고 생각하면 된다.
- -ac 1: 오디오 채널을 하나로 합쳐주는 옵션이다. 스테레오(2채널) 음악도 모노(1채널)로 바꿔준다.
- -ar 16000: 오디오 샘플링 레이트를 16000Hz로 설정하는 옵션이다. 이것도 Whisper가 좋아하는 옵션!
- check=True: ffmpeg이 일을 잘 끝냈는지 확인하는 옵션이다. 문제가 생기면 에러를 발생시켜서 알려준다.
- stderr=subprocess.PIPE: ffmpeg이 혹시나 에러 메시지를 뱉어내면, 그걸 잘 잡아두라는 옵션이다.
2. 오디오 파일 음성 인식(Whisper 사용): 이제부턴 Whisper 차례!
segments = transcribe_audio(audio_file)
-
transcribe_audio
함수의 실제 구현 내용은 이후에 소개할 것이지만 Whisper를 사용해서 오디오 파일을 텍스트로 바꿔주는 역할을 한다. 그리고 그 결과를 segments라는 변수에 저장한다. segments에는 "이 부분은 몇 초부터 몇 초까지 이 텍스트가 나왔어" 라는 정보가 들어있다.
3. SRT 자막 생성 파일: 자막 파일, 뚝딱 만들어 보자!
srt_content = create_srt_subtitle(segments)
with open(output_path, "w", encoding="utf-8") as f:
f.write(srt_content)
-
create_srt_subtitle
함수도 실제 구현 내용은 이후에 소개할 것이며, segments 정보를 예쁘게 다듬어서 SRT 형식의 자막 텍스트를 만들어준다. -
with open(...) as f:
는 파일을 열어서 작업하는 파이썬의 편리한 기능이다. output_path에 지정된 파일을 열어서("w"는 쓰기 모드라는 뜻이다), srt_content 내용을 저장한다. encoding="utf-8"은 한글이 깨지지 않도록 해주는 마법의 주문이다.
4. 임시 오디오 파일 삭제: 뒷정리도 깔끔하게!
os.remove(audio_file)
-
os.remote
는 파일을 삭제하는 함수이다. 이제 필요 없어진 임시 오디오 파일을 삭제한다.
5. 예외 처리(try...except): 혹시 모를 사고에 대비하자!
try:
# ... (위의 코드 블록) ...
except Exception as e:
print(f"오류 발생: {e}")
- try...except는 혹시나 코드 실행 중에 오류가 발생하면 프로그램이 뻗어버리지 않도록 안전하게 처리해 주는 역할을 한다. try 부분에서 코드를 실행하다가 오류가 나면, except 부분으로 넘어가서 오류 메시지를 출력하고 프로그램을 계속 진행한다.
transcribe_audio(audio_file)
함수: Whisper의 핵심 기능을 파헤쳐 보자!
이 함수는 Whisper 모델을 사용해서 오디오 파일을 텍스트로 변환하는, 즉 음성 인식 기능을 수행한다.
def transcribe_audio(audio_file):
model = whisper.load_model("base")
result = model.transcribe(audio_file)
return result["segments"]
-
model = whisper.load_model("base")
: Whisper 모델을 불러온다. "base"는 중간 크기의 모델을 사용한다는 뜻이다. Whisper는 크기별로 tiny, base, small, medium, large와 같은 다양한 모델을 제공하는데, 모델이 클수록 똑똑하지만(정확도가 높지만) 처리 속도는 느려지고 메모리도 많이 사용한다. - 모델 크기 비교 및 선택: 나에게 맞는 모델은?
모델 크기 | 파라미터 수 | 상대적 속도 | 메모리 요구량 |
---|---|---|---|
tiny | 39M | 가장 빠름 | ~1GB |
base | 74M | 빠름 | ~1GB |
small | 244M | 보통 | ~2GB |
medium | 769M | 느림 | ~5GB |
large | 1550M | 가장 느림 | ~10GB |
- 선택 가이드: 속도와 정확도 중 어느 쪽에 더 중점을 둘지 생각해서 모델을 선택하면 된다. 자막 생성 작업에서는 base나 small 모델이 적당한 선택일 수 있다. 컴퓨터 사양이 좋다면 medium이나 large 모델을 사용해서 더 정확한 결과를 얻을 수 있다.
-
return result["segments"]
: Whisper의 음성 인식 결과(result)에는 여러 정보가 담겨 있는데, 그 중에서 "segments" 부분만 쏙 뽑아서 돌려준다. "segments"에는 각 문장이 언제 시작하고 끝나는지에 대한 시간 정보와 변환된 텍스트가 들어있다.
create_srt_subtitle(segements)
함수: Whisper의 결과를 자막 형식으로 변신!
이 함수는 Whisper 모델에서 받은 "segements" 정보를 보기 좋은 SRT 자막 형식으로 바꿔주는 역할을 한다.
def create_srt_subtitle(segments):
srt_lines = []
for i, segment in enumerate(segments, start=1):
start_time = format_timestamp(segment["start"])
end_time = format_timestamp(segment["end"])
srt_lines.append(str(i))
srt_lines.append(f"{start_time} --> {end_time}")
srt_lines.append(segment["text"].strip())
srt_lines.append("")
return "\n".join(srt_lines)
-
srt_lines = []
: SRT 자막 파일에 들어갈 내용을 한 줄씩 저장할 빈 리스트를 만든다. -
for i, segments in enumerate(segments, start=1):
: segments 리스트를 하나씩 살펴보면서 반복 작업을 한다. enumerate는 각 항목의 순서(i)와 내용(segment)을 알려주는 편리한 함수이다. start=1은 순서를 1부터 세도록 하는 옵션이다. -
start_time = format_timestamp(segment["start"])
: segment에서 시작 시간 정보를 가져와서 SRT 형식에 맞게 바꿔준다. format_timestamp는 아래에서 다시 정리한다. -
end_time = format_timestamp(segment["end"])
: segment에서 종료 시간 정보를 가져와서 SRT 형식에 맞게 바꿔준다. -
srt_lines.append(...)
: SRT 형식에 맞춰서 다음 네 가지 정보를 srt_lines 리스트에 차례대로 추가한다.- 자막 번호(i)
- 시작 시간과 종료 시간(--> 로 구분)
- 자막 텍스트 segment["text"].strip(): 앞뒤에 혹시나 있을지 모르는 공백을 제거
- 빈 줄: 자막과 자막 사이를 구분하기 위해
-
return "\n".join(srt_lines)
: srt_lines 리스트에 있는 모든 내용을 줄바꿈 문자(\n)로 연결해서 하나의 커다란 문자열로 만들어 준다. 이 문자열이 바로 SRT 자막 파일의 내용이 된다.
format_timestamp(seconds)
함수: 시간 정보를 SRT 형식으로 예쁘게 단장하기!
이 함수는 초 단위로 된 시간 정보(예: 123.456초)를 SRT 자막 파일에서 사용하는 시간 형식(HH:MM:SS,mmm, 예: 00:02:03,456)으로 바꿔주는 역할을 한다.
def format_timestamp(seconds):
td = timedelta(seconds=seconds)
hours = td.seconds//3600
minutes = (td.seconds//60)%60
seconds = td.seconds%60
milliseconds = td.microseconds//1000
return f"{hours:02d}:{minutes:02d}:{seconds:02d},{milliseconds:03d}"
-
td = timedelta(seconds=seconds)
: datetime 라이브러리의 timedelta를 사용해서 시간 계산을 편리하게 한다. seconds에 주어진 초 값을 timedelta 객체로 만들어 td 변수에 저장한다. -
hours, minutes, seconds, milliseconds
: timedelta 객체에서 시간, 분, 초, 밀리초 값을 각각 뽑아낸다. -
return f"..."
: 뽑아낸 시간 정보를 SRT 형식에 맞게 조합해서 문자열로 돌려준다. :02d는 숫자를 2칸으로 표시하고, 빈칸은 0으로 채우라는 뜻이다. :03d는 밀리초를 3칸으로 표시한다.
if __name__ == "__main__"
: 내가 대장일 때만 일할거야!
이 부분은 파이썬 파일이 직접 실행될 때만 작동하는 코드이다. 다른 파이썬 파일에서 이 파일을 불러서 사용할 때는 이 부분의 코드는 실행되지 않는다.
if __name__ == "__main__":
video_path = "input_video.mp4"
output_path = "output.srt"
process_video(video_path, output_path)
-
video_path = "input_video.mp4"
: 자막을 만들 비디오 파일의 경로를 지정한다.(이 부분을 원하는 비디오 파일 경로로 바꿔야 한다.) -
output_path = "output.srt"
: 자막 파일을 저장할 경로를 지정한다. -
process_video(video_path, output_path)
: 위에서 설정한 process_video 함수를 호출해서 자막 생성 작업을 시작한다.
추가적으로 어떤 것을 더 해볼 수 있을까
이제 파이썬으로 비디오 자막을 자동으로 생성하는 멋진 프로그램을 만들 수 있게 되었다. 하지만 여기서 멈추지 말고 다음과 같은 어이디어를 더해서 프로그램을 더욱 발전시켜보는 것은 어떨까?
- 다양한 오디오 포맷 지원: ffmpeg을 잘 화룡하면 mp3, aac 등 다양한 오디오 포맷을 지원하도록 만들 수 있다.
- 다국어 지원: Whisper는 여러 언어를 인식할 수 있는 똑똑한 AI이다. 사용자가 원하는 언어를 선택할 수 있도록 옵션을 추가하고, 그에 맞는 Whisper 모델을 사용하면 다국어 자막도 생성할 수 있다.
Top comments (0)