상세 컨텐츠

본문 제목

업스케일링 해보기(2)

업스케일링

by zmo 2025. 11. 15. 21:56

본문

저번 업스케일링 해보기까지 완료했다면 가상환경에서 다음과 같은 명령어를 터미널에 입력해보자.

(venv) python inference_realesrgan_video.py -i video1.mp4 -n RealESRGAN_x4plus

inference_realesrgan_video.py를 실행하면 미리 옮겨놓은 video1.mp4파일을 업스케일링 하면서 다음과 같이 업스케일링 현황이 나온다.

 

하지만 이상한 점이 보인다. 바로 예상 시간이 무려 70시간 43분이나 된다. 샘플로 넣은 영상이 30분 전후 길이의 긴 영상임을 감안해도 70시간을 기다리기에는 수지타산이 맞지 않는다 할수 있다. 최적화가 필요하다.

이 최적화를 하려면 비디오의 기본적인 요소와 FFmpeg이 무엇인지에 대해 알아보아야 한다. 간단하게 FFmpeg를 통한 최적화는 어떻게 해야할지 아이디어만 알아보고 익숙해져야 하는 비디오의 요소를 중점으로 알아보자. 

 

 

오늘 공부할 내용

 

FFmpeg

코덱 (Codec) - '압축 언어'

오디오 (Audio) - '소리 복사'

FPS (Frames Per Second) - '재생 속도'

비트레이트 (Bitrate) - '데이터 할당량'

 

 


FFmpeg

 

FFmpeg은 거의 모든 종류의 비디오와 오디오 파일을 다룰 수 있는, 터미널(CMD) 기반의 무료 오픈소스 프로그램이다. 비디오를 자르고, 붙이고, 변환하고, 압축하고, 추출하는 등 못하는 게 거의 없다.

 

왜 지금 쓸까?

 

inference_realesrgan_video.py 스크립트는 OpenCV (cv2.VideoWriter)를 사용해 업스케일된 프레임을 비디오로 저장한다.

이 방식은 매우 느리고, 사용할 수 있는 코덱(압축 방식)이 제한적이며, 비디오 품질 제어가 어렵다.

핵심 아이디어는 "Python은 AI 계산만 하고, 비디오 저장은 전문가(FFmpeg)에게 맡기자"는 것이다.

스크립트는 이렇게 작동한다.

  1. (Python) 원본 비디오(video1.mp4)에서 프레임(그림)들을 한 장씩 읽어온다.
  2. (Python) 각 프레임을 AI로 업스케일링한다.
  3. (Python) 업스케일된 프레임을 results/frames/frame_00001.png처럼 개별 이미지 파일로 일단 저장한다. (비디오로 바로 합치지 않는다)
  4. (Python) 비디오의 오디오 트랙을 audio.mp3 같은 별도 파일로 추출해 둔다.
  5. (Python) 모든 프레임 처리가 끝나면, 터미널 명령(subprocess.run)을 통해 FFmpeg을 호출한다.
  6. (FFmpeg) frames 폴더의 모든 PNG 파일 + audio.mp3 파일을 합쳐서 video1_out.mp4라는 최종 비디오를 생성한다.

비디오 기본 지식 (FFmpeg 제어판)

코덱 (Codec) - '압축 언어'

실제 화질과 용량을 결정하는 핵심 기술이다. 코덱은 원본 비디오를 '어떤 언어'로 압축해서 저장할지 결정하는 알고리즘이다.

  • "안녕하세요"라는 5글자 정보를 저장한다고 생각해 보자.
    • 압축 안 함 (Raw Video): "안녕하세요"라고 5글자 그대로 저장한다. (화질은 완벽하지만 용량이 어마어마하다.)
    • H.264 (libx264) 코덱: "ㅇㄴㅎㅅㅇ"라고 자음만 저장한다. (용량이 줄어들지만, 원본과 거의 비슷하게 복원 가능. 가장 널리 쓰이는 표준 언어다.)
    • H.265 (HEVC) 코덱: "ㅇㄴ"이라고 초성 2개만 저장한다. (H.264보다 더 고효율 압축 언어. H.264와 같은 화질을 절반의 용량으로 구현할 수 있다. 하지만 이 '언어'를 못 알아듣는 구형 기기(TV)도 있다.)

FFmpeg을 쓸 때, 우리는 "PNG 프레임들을 H.264(libx264)라는 '언어'로 압축해서 .mp4 '그릇'에 담아줘"라고 명령하게 된다.

  • ffmpeg ... -c:v libx264 ... output.mp4
  • (c:v는 "비디오 코덱(Codec for Video)"이라는 뜻이다.)

 

오디오 (Audio) - '소리 복사'

Real-ESRGAN 스크립트는 이미지만 처리한다. 즉, 원본 비디오의 소리를 버린다.

  • 문제: 업스케일링 결과물은 '소리 없는' 벙어리 비디오가 돼버린다.
  • FFmpeg 해결책: 원본 비디오(input.mp4)에서 오디오 트랙만 '복사'해서, 업스케일된 비디오 트랙과 합칠 수 있다.

오래된 비디오테이프의 '화면'만 디지털로 복원(업스케일링)한 뒤, 원본 테이프의 '소리'는 그대로 가져와서 합치는 것과 같다.

  • 명령어:
    1. (Python) AI가 프레임들을 업스케일링해서 temp_video.mp4(소리 없음)를 만든다.
    2. (FFmpeg) ffmpeg -i temp_video.mp4 -i input.mp4 -c:v copy -c:a copy -map 0:v:0 -map 1:a:0 final_video.mp4
      • c:a copy: 오디오 코덱(a)을 다시 압축하지 말고 원본 그대로 복사(copy)해라.
      • map 0:v:0: 0번 입력(temp_video)의 비디오(v)를 가져와라.
      • map 1:a:0: 1번 입력(input.mp4)의 오디오(a)를 가져와라.

 

FPS (Frames Per Second) - '재생 속도'

FPS는 1초에 몇 장의 그림(프레임)을 보여줄지 정하는 값이다.

  • 문제: Python 스크립트가 비디오를 frame_001.png, frame_002.png...처럼 낱장 이미지로 분해하는 순간, "이 그림들이 원래 1초에 30장씩 재생되던 것"이라는 시간 정보가 사라진다.
  • FFmpeg 해결책: 이 낱장 이미지들을 다시 비디오로 합칠 때, 원본의 FPS 값을 반드시 알려줘야 한다.

300페이지짜리 만화책을 한 장씩 뜯어서 스캔(업스케일링)했다. 이걸 다시 책으로 묶을 때(FFmpeg) "이건 원래 1초에 30페이지씩 넘기던 거야" (framerate 30)라고 알려주지 않으면, FFmpeg은 자기 마음대로 1초에 25페이지씩 넘겨서(기본값) 재생 속도가 느려지게 된다.

 

비트레이트 (Bitrate) - '데이터 할당량'

비트레이트는 1초 분량의 비디오를 저장하는 데 얼마나 많은 데이터(bits)를 사용할지 정하는 값이다.

그림을 그릴 때 사용하는 '물감의 양'이라고 생각하면 편하다.

  • 비트레이트가 높다 (예: 10,000 kbps): 물감을 듬뿍 써서 그린다. -> 색이 풍부하고 디테일이 살아있다. (고화질, 큰 용량)
  • 비트레이트가 낮다 (예: 1,000 kbps): 물감을 아껴서(물을 많이 타서) 그린다. -> 색이 옅고 경계가 뭉개진다. (저화질, 작은 용량). 비디오에서는 '깍두기 현상(artifacts)'으로 나타난다.
  • 4단계 적용: 우리는 FFmpeg에게 "품질은 내가 정할 테니, 용량(비트레이트)은 네가 알아서 써라"고 할 수 있다.
    • 이것을 **CRF (Constant Rate Factor)**라고 한다.
    • ffmpeg ... -c:v libx264 -crf 23 ...
    • crf 23: H.264 코덱의 표준 품질 값이다. 숫자가 낮을수록 고화질(물감을 많이 씀)이고, 높을수록 저화질(물감을 아낌)이다.

관련글 더보기